libs-base/Source/NSPropertyList.m
theraven ceba92a265 Lots of little fixes to make -base compile with -Werror (now builds without warnings).
Richard: I'm unsure about three of these, which were fixes in memset() calls in:
- NSConcreteMapTable.m
- NSConcreteHashTable.m
- Additions/NSData+GNUstepBase.m

Please can you check them?  I think they are intended to zero the entire object
(rather than the first word), but the lack of comments makes me unsure.

Most changes were just tweaks to variable types.  I've also removed some dead code from NSInvocation.  This was small group of things that were marked for internal use only, but not actually referenced in the code anywhere.

Other improvements:

- NSArray / NSDictionary fixed up to use the 10.7 (ARC-friendly) prototypes.
- getObjects:andKeys: implemented for NSDictionary (10.5 method)
- NSPointerArray and NSHashTable now properly support weak objects.
- Tests for weak objects in collections.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@33621 72102866-910b-0410-8b05-ffd578937521
2011-07-24 13:09:22 +00:00

4050 lines
88 KiB
Objective-C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** Interface for NSPropertyList for GNUstep
Copyright (C) 2003,2004 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Fred Kiefer <FredKiefer@gmx.de>
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import "common.h"
#include <string.h>
#import "GNUstepBase/GSMime.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSAutoreleasePool.h"
#import "Foundation/NSByteOrder.h"
#import "Foundation/NSCalendarDate.h"
#import "Foundation/NSCharacterSet.h"
#import "Foundation/NSData.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSEnumerator.h"
#import "Foundation/NSError.h"
#import "Foundation/NSException.h"
#import "Foundation/NSPropertyList.h"
#import "Foundation/NSSerialization.h"
#import "Foundation/NSStream.h"
#import "Foundation/NSTimeZone.h"
#import "Foundation/NSUserDefaults.h"
#import "Foundation/NSValue.h"
#import "Foundation/NSNull.h"
#import "Foundation/NSXMLParser.h"
#import "GNUstepBase/Unicode.h"
#import "GNUstepBase/NSProcessInfo+GNUstepBase.h"
#import "GNUstepBase/NSObject+GNUstepBase.h"
#import "GNUstepBase/NSString+GNUstepBase.h"
#import "GSPrivate.h"
@class GSSloppyXMLParser;
#define inrange(ch,min,max) ((ch)>=(min) && (ch)<=(max))
#define char2num(ch) \
inrange(ch,'0','9') \
? ((ch)-0x30) \
: (inrange(ch,'a','f') \
? ((ch)-0x57) : ((ch)-0x37))
/*
* Cache classes.
*/
static Class NSArrayClass;
static Class NSDataClass;
static Class NSDateClass;
static Class NSDictionaryClass;
static Class NSNumberClass;
static Class NSStringClass;
static Class NSMutableStringClass;
static Class GSStringClass;
static Class GSMutableStringClass;
extern BOOL GSScanDouble(unichar*, unsigned, double*);
@class GSMutableDictionary;
@interface GSMutableDictionary : NSObject // Help the compiler
@end
@interface GSXMLPListParser : NSObject
{
NSXMLParser *theParser;
NSMutableString *value;
NSMutableArray *stack;
id key;
BOOL inArray;
BOOL inDictionary;
BOOL inString;
BOOL parsed;
BOOL success;
id plist;
NSPropertyListMutabilityOptions opts;
}
- (id) initWithData: (NSData*)data
mutability: (NSPropertyListMutabilityOptions)options;
- (BOOL) parse;
- (void) parser: (NSXMLParser *)parser
foundCharacters: (NSString *)string;
- (void) parser: (NSXMLParser *)parser
didStartElement: (NSString *)elementName
namespaceURI: (NSString *)namespaceURI
qualifiedName: (NSString *)qualifiedName
attributes: (NSDictionary *)attributeDict;
- (void) parser: (NSXMLParser *)parser
didEndElement: (NSString *)elementName
namespaceURI: (NSString *)namespaceURI
qualifiedName: (NSString *)qName;
- (id) result;
- (void) unescape;
@end
@interface GSSloppyXMLParser : NSXMLParser
@end
@implementation GSXMLPListParser
- (void) dealloc
{
RELEASE(key);
RELEASE(stack);
RELEASE(plist);
RELEASE(value);
RELEASE(theParser);
[super dealloc];
}
- (id) initWithData: (NSData*)data
mutability: (NSPropertyListMutabilityOptions)options
{
if ((self = [super init]) != nil)
{
theParser = [[GSSloppyXMLParser alloc] initWithData: data];
[theParser setDelegate: self];
opts = options;
}
return self;
}
- (void) parser: (NSXMLParser *)parser
foundCharacters: (NSString *)string
{
if (YES == inString)
{
[value appendString: string];
}
else
{
[value appendString: [string stringByTrimmingSpaces]];
}
}
- (void) parser: (NSXMLParser *)parser
foundIgnorableWhitespace: (NSString *)string
{
if (YES == inString)
{
[value appendString: string];
}
}
- (void) parser: (NSXMLParser *)parser
didStartElement: (NSString *)elementName
namespaceURI: (NSString *)namespaceURI
qualifiedName: (NSString *)qualifiedName
attributes: (NSDictionary *)attributeDict
{
if ([elementName isEqualToString: @"dict"] == YES)
{
NSMutableDictionary *d;
if (key == nil)
{
key = [[NSNull null] retain];
}
[stack addObject: key];
DESTROY(key);
d = [[NSMutableDictionary alloc] initWithCapacity: 10];
[stack addObject: d];
RELEASE(d);
inDictionary = YES;
inArray = NO;
}
else if ([elementName isEqualToString: @"array"] == YES)
{
NSMutableArray *a;
if (key == nil)
{
key = [[NSNull null] retain];
}
[stack addObject: key];
DESTROY(key);
a = [[NSMutableArray alloc] initWithCapacity: 10];
[stack addObject: a];
RELEASE(a);
inArray = YES;
inDictionary = NO;
}
else if ([elementName isEqualToString: @"string"] == YES)
{
inString = YES;
}
}
- (void) parser: (NSXMLParser *)parser
didEndElement: (NSString *)elementName
namespaceURI: (NSString *)namespaceURI
qualifiedName: (NSString *)qName
{
BOOL inContainer = NO;
inString = NO;
if ([elementName isEqualToString: @"dict"] == YES)
{
inContainer = YES;
}
else if ([elementName isEqualToString: @"array"] == YES)
{
inContainer = YES;
}
if (inContainer)
{
if (opts != NSPropertyListImmutable)
{
ASSIGN(plist, [stack lastObject]);
}
else
{
ASSIGN(plist, [[stack lastObject] makeImmutableCopyOnFail: NO]);
}
[stack removeLastObject];
inArray = NO;
inDictionary = NO;
ASSIGN(key, [stack lastObject]);
[stack removeLastObject];
if ((id)key == (id)[NSNull null])
{
DESTROY(key);
}
if ([stack count] > 0)
{
id last;
last = [stack lastObject];
if ([last isKindOfClass: NSArrayClass] == YES)
{
inArray = YES;
}
else if ([last isKindOfClass: NSDictionaryClass] == YES)
{
inDictionary = YES;
}
}
}
else if ([elementName isEqualToString: @"key"] == YES)
{
[self unescape];
ASSIGNCOPY(key, value);
[value setString: @""];
return;
}
else if ([elementName isEqualToString: @"data"])
{
NSData *d;
d = [GSMimeDocument decodeBase64:
[value dataUsingEncoding: NSASCIIStringEncoding]];
if (opts == NSPropertyListMutableContainersAndLeaves)
{
d = AUTORELEASE([d mutableCopy]);
}
ASSIGN(plist, d);
if (d == nil)
{
[parser abortParsing];
return;
}
}
else if ([elementName isEqualToString: @"date"])
{
id result;
if ([value hasSuffix: @"Z"] == YES && [value length] == 20)
{
result = [NSCalendarDate dateWithString: value
calendarFormat: @"%Y-%m-%dT%H:%M:%SZ"];
}
else
{
result = [NSCalendarDate dateWithString: value
calendarFormat: @"%Y-%m-%d %H:%M:%S %z"];
}
ASSIGN(plist, result);
}
else if ([elementName isEqualToString: @"string"])
{
id o;
[self unescape];
if (opts == NSPropertyListMutableContainersAndLeaves)
{
o = [value mutableCopy];
}
else
{
o = [value copy];
}
ASSIGN(plist, o);
[o release];
}
else if ([elementName isEqualToString: @"integer"])
{
ASSIGN(plist, [NSNumber numberWithLongLong: [value longLongValue]]);
}
else if ([elementName isEqualToString: @"real"])
{
ASSIGN(plist, [NSNumber numberWithDouble: [value doubleValue]]);
}
else if ([elementName isEqualToString: @"true"])
{
ASSIGN(plist, [NSNumber numberWithBool: YES]);
}
else if ([elementName isEqualToString: @"false"])
{
ASSIGN(plist, [NSNumber numberWithBool: NO]);
}
else if ([elementName isEqualToString: @"plist"])
{
[value setString: @""];
return;
}
else // invalid tag
{
NSLog(@"unrecognized tag <%@>", elementName);
[parser abortParsing];
return;
}
if (inArray == YES)
{
[[stack lastObject] addObject: plist];
}
else if (inDictionary == YES)
{
if (key == nil)
{
[parser abortParsing];
return;
}
[(NSMutableDictionary*)[stack lastObject] setObject: plist forKey: key];
DESTROY(key);
}
[value setString: @""];
}
- (BOOL) parse
{
if (parsed == NO)
{
parsed = YES;
stack = [[NSMutableArray alloc] initWithCapacity: 10];
value = [[NSMutableString alloc] initWithCapacity: 50];
success = [theParser parse];
}
return success;
}
- (id) result
{
return plist;
}
- (void) unescape
{
id o;
NSRange r;
/* Convert any \Uxxxx sequences to unicode characters.
*/
r = NSMakeRange(0, [value length]);
while (r.length >= 6)
{
r = [value rangeOfString: @"\\U" options: NSLiteralSearch range: r];
if (r.length == 2 && [value length] >= r.location + 6)
{
unichar c;
unichar v;
c = [value characterAtIndex: r.location + 2];
if (isxdigit(c))
{
v = char2num(c);
c = [value characterAtIndex: r.location + 3];
if (isxdigit(c))
{
v <<= 4;
v |= char2num(c);
c = [value characterAtIndex: r.location + 4];
if (isxdigit(c))
{
v <<= 4;
v |= char2num(c);
c = [value characterAtIndex: r.location + 5];
if (isxdigit(c))
{
v <<= 4;
v |= char2num(c);
o = [NSString alloc];
o = [o initWithCharacters: &v length: 1];
r.length += 4;
[value replaceCharactersInRange: r withString: o];
[o release];
r.location++;
r.length = 0;
}
}
}
}
r = NSMakeRange(NSMaxRange(r), [value length] - NSMaxRange(r));
}
}
}
@end
@interface GSBinaryPLParser : NSObject
{
NSPropertyListMutabilityOptions mutability;
const unsigned char *_bytes;
NSData *data;
unsigned offset_size; // Number of bytes per table entry
unsigned index_size; // Number of bytes per table entry
unsigned object_count; // Number of objects
unsigned root_index; // Index of root object
unsigned table_start; // Start address of object table
}
- (id) initWithData: (NSData*)plData
mutability: (NSPropertyListMutabilityOptions)m;
- (id) rootObject;
- (id) objectAtIndex: (NSUInteger)index;
@end
@interface GSBinaryPLGenerator : NSObject
{
NSMutableData *dest;
NSMapTable *objectList;
NSMutableArray *objectsToDoList;
id root;
// Number of bytes per object table index
unsigned int index_size;
// Number of bytes per object table entry
unsigned int offset_size;
unsigned int table_start;
unsigned int table_size;
unsigned int *table;
}
+ (void) serializePropertyList: (id)aPropertyList intoData: (NSMutableData *)destination;
- (id) initWithPropertyList: (id)aPropertyList intoData: (NSMutableData *)destination;
- (void) generate;
- (void) storeObject: (id)object;
- (void) cleanup;
@end
static Class plArray;
static id (*plAdd)(id, SEL, id) = 0;
static Class plDictionary;
static id (*plSet)(id, SEL, id, id) = 0;
/* Bitmap of 'quotable' characters ... those characters which must be
* inside a quoted string if written to an old style property list.
*/
static const unsigned char quotables[32] = {
'\xff',
'\xff',
'\xff',
'\xff',
'\x85',
'\x13',
'\x00',
'\x78',
'\x00',
'\x00',
'\x00',
'\x38',
'\x01',
'\x00',
'\x00',
'\xa8',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
'\xff',
};
/* Bitmap of characters considered white space if in an old style property
* list. This is the same as the set given by the isspace() function in the
* POSIX locale, but (for cross-locale portability of property list files)
* is fixed, rather than locale dependent.
*/
static const unsigned char whitespace[32] = {
'\x00',
'\x3f',
'\x00',
'\x00',
'\x01',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
'\x00',
};
#define IS_BIT_SET(a,i) ((((a) & (1<<(i)))) > 0)
#define GS_IS_QUOTABLE(X) IS_BIT_SET(quotables[(X)/8], (X) % 8)
#define GS_IS_WHITESPACE(X) IS_BIT_SET(whitespace[(X)/8], (X) % 8)
static NSCharacterSet *oldQuotables = nil;
static NSCharacterSet *xmlQuotables = nil;
static void setupQuotables(void)
{
if (oldQuotables == nil)
{
NSMutableCharacterSet *s;
/* The '$', '.', '/' and '_' characters used to be OK to use in
* property lists, but OSX now quotes them, so we follow suite.
*/
s = [[NSCharacterSet characterSetWithCharactersInString:
@"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@"abcdefghijklmnopqrstuvwxyz"]
mutableCopy];
[s invert];
oldQuotables = [s copy];
RELEASE(s);
s = [[NSCharacterSet characterSetWithCharactersInString:
@"&<>'\\\""] mutableCopy];
[s addCharactersInRange: NSMakeRange(0x0001, 0x001f)];
[s removeCharactersInRange: NSMakeRange(0x0009, 0x0002)];
[s removeCharactersInRange: NSMakeRange(0x000D, 0x0001)];
[s addCharactersInRange: NSMakeRange(0xD800, 0x07FF)];
[s addCharactersInRange: NSMakeRange(0xFFFE, 0x0002)];
xmlQuotables = [s copy];
RELEASE(s);
}
}
typedef struct {
const unsigned char *ptr;
unsigned end;
unsigned pos;
unsigned lin;
NSString *err;
NSPropertyListMutabilityOptions opt;
BOOL key;
BOOL old;
} pldata;
/*
* Property list parsing - skip whitespace keeping count of lines and
* regarding objective-c style comments as whitespace.
* Returns YES if there is any non-whitespace text remaining.
*/
static BOOL skipSpace(pldata *pld)
{
unsigned char c;
while (pld->pos < pld->end)
{
c = pld->ptr[pld->pos];
if (GS_IS_WHITESPACE(c) == NO)
{
if (c == '/' && pld->pos < pld->end - 1)
{
/*
* Check for comments beginning '/' followed by '/' or '*'
*/
if (pld->ptr[pld->pos + 1] == '/')
{
pld->pos += 2;
while (pld->pos < pld->end)
{
c = pld->ptr[pld->pos];
if (c == '\n')
{
break;
}
pld->pos++;
}
if (pld->pos >= pld->end)
{
pld->err = @"reached end of string in comment";
return NO;
}
}
else if (pld->ptr[pld->pos + 1] == '*')
{
pld->pos += 2;
while (pld->pos < pld->end)
{
c = pld->ptr[pld->pos];
if (c == '\n')
{
pld->lin++;
}
else if (c == '*' && pld->pos < pld->end - 1
&& pld->ptr[pld->pos+1] == '/')
{
pld->pos++; /* Skip past '*' */
break;
}
pld->pos++;
}
if (pld->pos >= pld->end)
{
pld->err = @"reached end of string in comment";
return NO;
}
}
else
{
return YES;
}
}
else
{
return YES;
}
}
if (c == '\n')
{
pld->lin++;
}
pld->pos++;
}
pld->err = @"reached end of string";
return NO;
}
static inline id parseQuotedString(pldata* pld)
{
unsigned start = ++pld->pos;
unsigned escaped = 0;
unsigned shrink = 0;
BOOL hex = NO;
NSString *obj;
while (pld->pos < pld->end)
{
unsigned char c = pld->ptr[pld->pos];
if (escaped)
{
if (escaped == 1 && c >= '0' && c <= '7')
{
escaped = 2;
hex = NO;
}
else if (escaped == 1 && (c == 'u' || c == 'U'))
{
escaped = 2;
hex = YES;
}
else if (escaped > 1)
{
if (hex && isxdigit(c))
{
shrink++;
escaped++;
if (escaped == 6)
{
escaped = 0;
}
}
else if (c >= '0' && c <= '7')
{
shrink++;
escaped++;
if (escaped == 4)
{
escaped = 0;
}
}
else
{
pld->pos--;
escaped = 0;
}
}
else
{
escaped = 0;
}
}
else
{
if (c == '\\')
{
escaped = 1;
shrink++;
}
else if (c == '"')
{
break;
}
}
if (c == '\n')
pld->lin++;
pld->pos++;
}
if (pld->pos >= pld->end)
{
pld->err = @"reached end of string while parsing quoted string";
return nil;
}
if (pld->pos - start - shrink == 0)
{
obj = @"";
}
else
{
unsigned length;
unichar *chars;
unichar *temp = NULL;
unsigned int temp_length = 0;
unsigned j;
unsigned k;
if (!GSToUnicode(&temp, &temp_length, &pld->ptr[start],
pld->pos - start, NSUTF8StringEncoding,
NSDefaultMallocZone(), 0))
{
pld->err = @"invalid utf8 data while parsing quoted string";
return nil;
}
length = temp_length - shrink;
chars = NSAllocateCollectable(sizeof(unichar) * length, 0);
escaped = 0;
hex = NO;
for (j = 0, k = 0; j < temp_length; j++)
{
unichar c = temp[j];
if (escaped)
{
if (escaped == 1 && c >= '0' && c <= '7')
{
chars[k] = c - '0';
hex = NO;
escaped++;
}
else if (escaped == 1 && (c == 'u' || c == 'U'))
{
chars[k] = 0;
hex = YES;
escaped++;
}
else if (escaped > 1)
{
if (hex && isxdigit(c))
{
chars[k] <<= 4;
chars[k] |= char2num(c);
escaped++;
if (escaped == 6)
{
escaped = 0;
k++;
}
}
else if (c >= '0' && c <= '7')
{
chars[k] <<= 3;
chars[k] |= (c - '0');
escaped++;
if (escaped == 4)
{
escaped = 0;
k++;
}
}
else
{
escaped = 0;
j--;
k++;
}
}
else
{
escaped = 0;
switch (c)
{
case 'a' : chars[k] = '\a'; break;
case 'b' : chars[k] = '\b'; break;
case 't' : chars[k] = '\t'; break;
case 'r' : chars[k] = '\r'; break;
case 'n' : chars[k] = '\n'; break;
case 'v' : chars[k] = '\v'; break;
case 'f' : chars[k] = '\f'; break;
default : chars[k] = c; break;
}
k++;
}
}
else
{
chars[k] = c;
if (c == '\\')
{
escaped = 1;
}
else
{
k++;
}
}
}
NSZoneFree(NSDefaultMallocZone(), temp);
length = k;
if (pld->key ==
NO && pld->opt == NSPropertyListMutableContainersAndLeaves)
{
obj = [GSMutableString alloc];
obj = [obj initWithCharactersNoCopy: chars
length: length
freeWhenDone: YES];
}
else
{
obj = [GSMutableString alloc];
obj = [obj initWithCharactersNoCopy: chars
length: length
freeWhenDone: YES];
}
}
pld->pos++;
return obj;
}
static inline id parseUnquotedString(pldata *pld)
{
unsigned start = pld->pos;
unsigned i;
unsigned length;
id obj;
unichar *chars;
while (pld->pos < pld->end)
{
if (GS_IS_QUOTABLE(pld->ptr[pld->pos]) == YES)
break;
pld->pos++;
}
length = pld->pos - start;
chars = NSAllocateCollectable(sizeof(unichar) * length, 0);
for (i = 0; i < length; i++)
{
chars[i] = pld->ptr[start + i];
}
if (pld->key == NO && pld->opt == NSPropertyListMutableContainersAndLeaves)
{
obj = [GSMutableString alloc];
obj = [obj initWithCharactersNoCopy: chars
length: length
freeWhenDone: YES];
}
else
{
obj = [GSMutableString alloc];
obj = [obj initWithCharactersNoCopy: chars
length: length
freeWhenDone: YES];
}
return obj;
}
static id parsePlItem(pldata* pld)
{
id result = nil;
BOOL start = (pld->pos == 0 ? YES : NO);
if (skipSpace(pld) == NO)
{
return nil;
}
switch (pld->ptr[pld->pos])
{
case '{':
{
NSMutableDictionary *dict;
dict = [[plDictionary allocWithZone: NSDefaultMallocZone()]
initWithCapacity: 0];
pld->pos++;
while (skipSpace(pld) == YES && pld->ptr[pld->pos] != '}')
{
id key;
id val;
pld->key = YES;
key = parsePlItem(pld);
pld->key = NO;
if (key == nil)
{
return nil;
}
if (skipSpace(pld) == NO)
{
RELEASE(key);
RELEASE(dict);
return nil;
}
if (pld->ptr[pld->pos] != '=')
{
pld->err = @"unexpected character (wanted '=')";
RELEASE(key);
RELEASE(dict);
return nil;
}
pld->pos++;
val = parsePlItem(pld);
if (val == nil)
{
RELEASE(key);
RELEASE(dict);
return nil;
}
if (skipSpace(pld) == NO)
{
RELEASE(key);
RELEASE(val);
RELEASE(dict);
return nil;
}
if (pld->ptr[pld->pos] == ';')
{
pld->pos++;
}
else if (pld->ptr[pld->pos] == '}')
{
if (GSPrivateDefaultsFlag(GSMacOSXCompatible))
{
pld->err = @"unexpected character '}' (wanted ';')";
RELEASE(key);
RELEASE(val);
RELEASE(dict);
return nil;
}
else
{
NSWarnFLog(
@"Missing semicolon in dictionary at line %d char %d",
pld->lin + 1, pld->pos + 1);
}
}
else
{
pld->err = @"unexpected character (wanted ';' or '}')";
RELEASE(key);
RELEASE(val);
RELEASE(dict);
return nil;
}
(*plSet)(dict, @selector(setObject:forKey:), val, key);
RELEASE(key);
RELEASE(val);
}
if (pld->pos >= pld->end)
{
pld->err = @"unexpected end of string when parsing dictionary";
RELEASE(dict);
return nil;
}
pld->pos++;
result = dict;
if (pld->opt == NSPropertyListImmutable)
{
[result makeImmutableCopyOnFail: NO];
}
}
break;
case '(':
{
NSMutableArray *array;
array = [[plArray allocWithZone: NSDefaultMallocZone()]
initWithCapacity: 0];
pld->pos++;
while (skipSpace(pld) == YES && pld->ptr[pld->pos] != ')')
{
id val;
val = parsePlItem(pld);
if (val == nil)
{
RELEASE(array);
return nil;
}
if (skipSpace(pld) == NO)
{
RELEASE(val);
RELEASE(array);
return nil;
}
if (pld->ptr[pld->pos] == ',')
{
pld->pos++;
}
else if (pld->ptr[pld->pos] != ')')
{
pld->err = @"unexpected character (wanted ',' or ')')";
RELEASE(val);
RELEASE(array);
return nil;
}
(*plAdd)(array, @selector(addObject:), val);
RELEASE(val);
}
if (pld->pos >= pld->end)
{
pld->err = @"unexpected end of string when parsing array";
RELEASE(array);
return nil;
}
pld->pos++;
result = array;
if (pld->opt == NSPropertyListImmutable)
{
[result makeImmutableCopyOnFail: NO];
}
}
break;
case '<':
pld->pos++;
if (pld->pos < pld->end && pld->ptr[pld->pos] == '*')
{
const unsigned char *ptr;
unsigned min;
unsigned len = 0;
unsigned i;
pld->old = NO;
pld->pos++;
min = pld->pos;
ptr = &(pld->ptr[min]);
while (pld->pos < pld->end && pld->ptr[pld->pos] != '>')
{
pld->pos++;
}
len = pld->pos - min;
if (len > 1)
{
unsigned char type = *ptr++;
len--;
// Allow for quoted values.
if (ptr[0] == '"' && len > 1)
{
len--;
ptr++;
if (ptr[len - 1] == '"')
{
len--;
}
}
if (type == 'I')
{
char buf[len+1];
for (i = 0; i < len; i++) buf[i] = (char)ptr[i];
buf[len] = '\0';
result = [[NSNumber alloc] initWithLongLong: atoll(buf)];
}
else if (type == 'B')
{
if (ptr[0] == 'Y')
{
result = [[NSNumber alloc] initWithBool: YES];
}
else if (ptr[0] == 'N')
{
result = [[NSNumber alloc] initWithBool: NO];
}
else
{
pld->err = @"bad value for bool";
return nil;
}
}
else if (type == 'D')
{
unichar buf[len];
unsigned i;
NSString *str;
for (i = 0; i < len; i++) buf[i] = ptr[i];
str = [[NSString alloc] initWithCharacters: buf
length: len];
result = [[NSCalendarDate alloc] initWithString: str
calendarFormat: @"%Y-%m-%d %H:%M:%S %z"];
RELEASE(str);
}
else if (type == 'R')
{
unichar buf[len];
double d = 0.0;
for (i = 0; i < len; i++) buf[i] = ptr[i];
GSScanDouble(buf, len, &d);
result = [[NSNumber alloc] initWithDouble: d];
}
else
{
pld->err = @"unrecognized type code after '<*'";
return nil;
}
}
else
{
pld->err = @"missing type code after '<*'";
return nil;
}
if (pld->pos >= pld->end)
{
pld->err = @"unexpected end of string when parsing data";
return nil;
}
if (pld->ptr[pld->pos] != '>')
{
pld->err = @"unexpected character (wanted '>')";
return nil;
}
pld->pos++;
}
else
{
NSMutableData *data;
unsigned max = pld->end - 1;
unsigned char buf[BUFSIZ];
unsigned len = 0;
data = [[NSMutableData alloc] initWithCapacity: 0];
skipSpace(pld);
while (pld->pos < max
&& isxdigit(pld->ptr[pld->pos])
&& isxdigit(pld->ptr[pld->pos+1]))
{
unsigned char byte;
byte = (char2num(pld->ptr[pld->pos])) << 4;
pld->pos++;
byte |= char2num(pld->ptr[pld->pos]);
pld->pos++;
buf[len++] = byte;
if (len == sizeof(buf))
{
[data appendBytes: buf length: len];
len = 0;
}
skipSpace(pld);
}
if (pld->pos >= pld->end)
{
pld->err = @"unexpected end of string when parsing data";
RELEASE(data);
return nil;
}
if (pld->ptr[pld->pos] != '>')
{
pld->err = @"unexpected character (wanted '>')";
RELEASE(data);
return nil;
}
if (len > 0)
{
[data appendBytes: buf length: len];
}
pld->pos++;
// FIXME ... should be immutable sometimes.
result = data;
}
break;
case '"':
result = parseQuotedString(pld);
break;
default:
result = parseUnquotedString(pld);
break;
}
if (start == YES && result != nil)
{
if (skipSpace(pld) == YES)
{
pld->err = @"extra data after parsed string";
result = nil; // Not at end of string.
}
}
return result;
}
id
GSPropertyListFromStringsFormat(NSString *string)
{
NSMutableDictionary *dict;
pldata _pld;
pldata *pld = &_pld;
NSData *d;
/*
* An empty string is a nil property list.
*/
if ([string length] == 0)
{
return nil;
}
d = [string dataUsingEncoding: NSUTF8StringEncoding];
NSCAssert(d, @"Couldn't get utf8 data from string.");
_pld.ptr = (unsigned char*)[d bytes];
_pld.pos = 0;
_pld.end = [d length];
_pld.err = nil;
_pld.lin = 0;
_pld.opt = NSPropertyListImmutable;
_pld.key = NO;
_pld.old = YES; // OpenStep style
[NSPropertyListSerialization class]; // initialise
dict = [[plDictionary allocWithZone: NSDefaultMallocZone()]
initWithCapacity: 0];
while (skipSpace(pld) == YES)
{
id key;
id val;
if (pld->ptr[pld->pos] == '"')
{
key = parseQuotedString(pld);
}
else
{
key = parseUnquotedString(pld);
}
if (key == nil)
{
DESTROY(dict);
break;
}
if (skipSpace(pld) == NO)
{
pld->err = @"incomplete final entry (no semicolon?)";
RELEASE(key);
DESTROY(dict);
break;
}
if (pld->ptr[pld->pos] == ';')
{
pld->pos++;
(*plSet)(dict, @selector(setObject:forKey:), @"", key);
RELEASE(key);
}
else if (pld->ptr[pld->pos] == '=')
{
pld->pos++;
if (skipSpace(pld) == NO)
{
RELEASE(key);
DESTROY(dict);
break;
}
if (pld->ptr[pld->pos] == '"')
{
val = parseQuotedString(pld);
}
else
{
val = parseUnquotedString(pld);
}
if (val == nil)
{
RELEASE(key);
DESTROY(dict);
break;
}
if (skipSpace(pld) == NO)
{
pld->err = @"missing final semicolon";
RELEASE(key);
RELEASE(val);
DESTROY(dict);
break;
}
(*plSet)(dict, @selector(setObject:forKey:), val, key);
RELEASE(key);
RELEASE(val);
if (pld->ptr[pld->pos] == ';')
{
pld->pos++;
}
else
{
pld->err = @"unexpected character (wanted ';')";
DESTROY(dict);
break;
}
}
else
{
pld->err = @"unexpected character (wanted '=' or ';')";
RELEASE(key);
DESTROY(dict);
break;
}
}
if (dict == nil && _pld.err != nil)
{
RELEASE(dict);
[NSException raise: NSGenericException
format: @"Parse failed at line %d (char %d) - %@",
_pld.lin + 1, _pld.pos + 1, _pld.err];
}
return AUTORELEASE(dict);
}
#include <math.h>
static char base64[]
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static void
encodeBase64(NSData *source, NSMutableData *dest)
{
int length = [source length];
int enclen = length / 3;
int remlen = length - 3 * enclen;
int destlen = 4 * ((length + 2) / 3);
unsigned char *sBuf;
unsigned char *dBuf;
int sIndex = 0;
int dIndex = [dest length];
[dest setLength: dIndex + destlen];
if (length == 0)
{
return;
}
sBuf = (unsigned char*)[source bytes];
dBuf = [dest mutableBytes];
for (sIndex = 0; sIndex < length - 2; sIndex += 3, dIndex += 4)
{
dBuf[dIndex] = base64[sBuf[sIndex] >> 2];
dBuf[dIndex + 1]
= base64[((sBuf[sIndex] << 4) | (sBuf[sIndex + 1] >> 4)) & 0x3f];
dBuf[dIndex + 2]
= base64[((sBuf[sIndex + 1] << 2) | (sBuf[sIndex + 2] >> 6)) & 0x3f];
dBuf[dIndex + 3] = base64[sBuf[sIndex + 2] & 0x3f];
}
if (remlen == 1)
{
dBuf[dIndex] = base64[sBuf[sIndex] >> 2];
dBuf[dIndex + 1] = (sBuf[sIndex] << 4) & 0x30;
dBuf[dIndex + 1] = base64[dBuf[dIndex + 1]];
dBuf[dIndex + 2] = '=';
dBuf[dIndex + 3] = '=';
}
else if (remlen == 2)
{
dBuf[dIndex] = base64[sBuf[sIndex] >> 2];
dBuf[dIndex + 1] = (sBuf[sIndex] << 4) & 0x30;
dBuf[dIndex + 1] |= sBuf[sIndex + 1] >> 4;
dBuf[dIndex + 1] = base64[dBuf[dIndex + 1]];
dBuf[dIndex + 2] = (sBuf[sIndex + 1] << 2) & 0x3c;
dBuf[dIndex + 2] = base64[dBuf[dIndex + 2]];
dBuf[dIndex + 3] = '=';
}
}
static inline void Append(void *bytes, unsigned length, NSMutableData *dst)
{
[dst appendBytes: bytes length: length];
}
/*
* Output a string escaped for OpenStep style property lists.
* The result is ascii data.
*/
static void
PString(NSString *obj, NSMutableData *output)
{
unsigned length;
if ((length = [obj length]) == 0)
{
[output appendBytes: "\"\"" length: 2];
}
else if ([obj rangeOfCharacterFromSet: oldQuotables].length > 0
|| [obj characterAtIndex: 0] == '/')
{
unichar *from;
unichar *end;
unsigned char *ptr;
int base = [output length];
int len = 0;
GS_BEGINITEMBUF(ustring, (length * sizeof(unichar)), unichar)
end = &ustring[length];
[obj getCharacters: ustring];
for (from = ustring; from < end; from++)
{
switch (*from)
{
case '\t':
case '\r':
case '\n':
len++;
break;
case '\a':
case '\b':
case '\v':
case '\f':
case '\\':
case '"' :
len += 2;
break;
default:
if (*from < 128)
{
if (isprint(*from) || *from == ' ')
{
len++;
}
else
{
len += 4;
}
}
else
{
len += 6;
}
break;
}
}
[output setLength: base + len + 2];
ptr = [output mutableBytes] + base;
*ptr++ = '"';
for (from = ustring; from < end; from++)
{
switch (*from)
{
case '\t':
case '\r':
case '\n':
*ptr++ = *from;
break;
case '\a': *ptr++ = '\\'; *ptr++ = 'a'; break;
case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break;
case '\v': *ptr++ = '\\'; *ptr++ = 'v'; break;
case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break;
case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break;
case '"' : *ptr++ = '\\'; *ptr++ = '"'; break;
default:
if (*from < 128)
{
if (isprint(*from) || *from == ' ')
{
*ptr++ = *from;
}
else
{
unichar c = *from;
*ptr++ = '\\';
ptr[2] = (c & 7) + '0';
c >>= 3;
ptr[1] = (c & 7) + '0';
c >>= 3;
ptr[0] = (c & 7) + '0';
ptr += 3;
}
}
else
{
unichar c = *from;
*ptr++ = '\\';
*ptr++ = 'U';
ptr[3] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48;
c >>= 4;
ptr[2] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48;
c >>= 4;
ptr[1] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48;
c >>= 4;
ptr[0] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48;
ptr += 4;
}
break;
}
}
*ptr = '"';
GS_ENDITEMBUF();
}
else
{
NSData *d = [obj dataUsingEncoding: NSASCIIStringEncoding];
[output appendData: d];
}
}
/*
* Output a string escaped for use in xml.
* Result is utf8 data.
*/
static void
XString(NSString* obj, NSMutableData *output)
{
static const char *hexdigits = "0123456789ABCDEF";
unsigned end;
end = [obj length];
if (end == 0)
{
return;
}
if ([obj rangeOfCharacterFromSet: xmlQuotables].length > 0)
{
unichar *base;
unichar *map;
unichar c;
unsigned len;
unsigned rpos;
unsigned wpos;
BOOL osx;
osx = GSPrivateDefaultsFlag(GSMacOSXCompatible);
base = NSAllocateCollectable(sizeof(unichar) * end, 0);
[obj getCharacters: base];
for (len = rpos = 0; rpos < end; rpos++)
{
c = base[rpos];
switch (c)
{
case '&':
len += 5;
break;
case '<':
case '>':
len += 4;
break;
case '\'':
case '"':
len += 6;
break;
default:
if ((c < 0x20 && (c != 0x09 && c != 0x0A && c != 0x0D))
|| (c > 0xD7FF && c < 0xE000) || c > 0xFFFD)
{
if (osx)
{
len += 8; // Illegal in XML
}
else
{
len += 6; // Non-standard escape
}
}
else
{
len++;
}
break;
}
}
map = NSAllocateCollectable(sizeof(unichar) * len, 0);
for (wpos = rpos = 0; rpos < end; rpos++)
{
c = base[rpos];
switch (c)
{
case '&':
map[wpos++] = '&';
map[wpos++] = 'a';
map[wpos++] = 'm';
map[wpos++] = 'p';
map[wpos++] = ';';
break;
case '<':
map[wpos++] = '&';
map[wpos++] = 'l';
map[wpos++] = 't';
map[wpos++] = ';';
break;
case '>':
map[wpos++] = '&';
map[wpos++] = 'g';
map[wpos++] = 't';
map[wpos++] = ';';
break;
case '\'':
map[wpos++] = '&';
map[wpos++] = 'a';
map[wpos++] = 'p';
map[wpos++] = 'o';
map[wpos++] = 's';
map[wpos++] = ';';
break;
case '"':
map[wpos++] = '&';
map[wpos++] = 'q';
map[wpos++] = 'u';
map[wpos++] = 'o';
map[wpos++] = 't';
map[wpos++] = ';';
break;
default:
if ((c < 0x20 && (c != 0x09 && c != 0x0A && c != 0x0D))
|| (c > 0xD7FF && c < 0xE000) || c > 0xFFFD)
{
if (osx)
{
/* Use XML style character entity references for
* OSX compatibility, even though this is an
* illegal character code and a standards complient
* XML parser will barf when it tries to read it.
* The OSX property list parser does not implement
* the XML standard and accepts at least some
* illegal characters.
*/
map[wpos++] = '&';
map[wpos++] = '#';
map[wpos++] = 'x';
map[wpos++] = hexdigits[(c>>12) & 0xf];
map[wpos++] = hexdigits[(c>>8) & 0xf];
map[wpos++] = hexdigits[(c>>4) & 0xf];
map[wpos++] = hexdigits[c & 0xf];
map[wpos++] = ';';
}
else
{
/* We need to be able to encode characters in a
* property list which are illegal in XML (even
* when encoded as numeric entities with the
* &#...; format. So we use the same \Uxxxx
* format is in old style property lists.
*/
map[wpos++] = '\\';
map[wpos++] = 'U';
map[wpos++] = hexdigits[(c>>12) & 0xf];
map[wpos++] = hexdigits[(c>>8) & 0xf];
map[wpos++] = hexdigits[(c>>4) & 0xf];
map[wpos++] = hexdigits[c & 0xf];
}
}
else
{
map[wpos++] = c;
}
break;
}
}
NSZoneFree(NSDefaultMallocZone(), base);
obj = [[NSString alloc] initWithCharacters: map length: len];
NSZoneFree(NSDefaultMallocZone(), map);
[output appendData: [obj dataUsingEncoding: NSUTF8StringEncoding]];
RELEASE(obj);
}
else
{
[output appendData: [obj dataUsingEncoding: NSUTF8StringEncoding]];
}
}
static const char *indentStrings[] = {
"",
" ",
" ",
" ",
"\t",
"\t ",
"\t ",
"\t ",
"\t\t",
"\t\t ",
"\t\t ",
"\t\t ",
"\t\t\t",
"\t\t\t ",
"\t\t\t ",
"\t\t\t ",
"\t\t\t\t",
"\t\t\t\t ",
"\t\t\t\t ",
"\t\t\t\t ",
"\t\t\t\t\t",
"\t\t\t\t\t ",
"\t\t\t\t\t ",
"\t\t\t\t\t ",
"\t\t\t\t\t\t"
};
/**
* obj is the object to be written out<br />
* loc is the locale for formatting (or nil to indicate no formatting)<br />
* lev is the level of indentation to use<br />
* step is the indentation step (0 == 0, 1 = 2, 2 = 4, 3 = 8)<br />
* x is an indicator for xml or old/new openstep property list format<br />
* dest is the output buffer.
*/
static void
OAppend(id obj, NSDictionary *loc, unsigned lev, unsigned step,
NSPropertyListFormat x, NSMutableData *dest)
{
if (NSStringClass == 0)
{
[NSPropertyListSerialization class]; // Force initialisation
}
if ([obj isKindOfClass: NSStringClass])
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<string>" length: 8];
XString(obj, dest);
[dest appendBytes: "</string>\n" length: 10];
}
else
{
PString(obj, dest);
}
}
else if ([obj isKindOfClass: NSNumberClass])
{
const char *t = [obj objCType];
if (*t == 'c' || *t == 'C')
{
BOOL val = [obj boolValue];
if (val == YES)
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<true/>\n" length: 8];
}
else if (x == NSPropertyListGNUstepFormat)
{
[dest appendBytes: "<*BY>" length: 5];
}
else
{
PString([obj description], dest);
}
}
else
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<false/>\n" length: 9];
}
else if (x == NSPropertyListGNUstepFormat)
{
[dest appendBytes: "<*BN>" length: 5];
}
else
{
PString([obj description], dest);
}
}
}
else if (strchr("sSiIlLqQ", *t) != 0)
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<integer>" length: 9];
XString([obj stringValue], dest);
[dest appendBytes: "</integer>\n" length: 11];
}
else if (x == NSPropertyListGNUstepFormat)
{
[dest appendBytes: "<*I" length: 3];
[dest appendData:
[[obj stringValue] dataUsingEncoding: NSASCIIStringEncoding]];
[dest appendBytes: ">" length: 1];
}
else
{
PString([obj description], dest);
}
}
else
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<real>" length: 6];
XString([obj stringValue], dest);
[dest appendBytes: "</real>\n" length: 8];
}
else if (x == NSPropertyListGNUstepFormat)
{
[dest appendBytes: "<*R" length: 3];
[dest appendData:
[[obj stringValue] dataUsingEncoding: NSASCIIStringEncoding]];
[dest appendBytes: ">" length: 1];
}
else
{
PString([obj description], dest);
}
}
}
else if ([obj isKindOfClass: NSDataClass])
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<data>\n" length: 7];
encodeBase64(obj, dest);
[dest appendBytes: "</data>\n" length: 8];
}
else
{
const unsigned char *src;
unsigned char *dst;
int length;
int i;
int j;
src = [obj bytes];
length = [obj length];
#define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
j = [dest length];
[dest setLength: j + 2*length+(length > 4 ? (length-1)/4+2 : 2)];
dst = [dest mutableBytes];
dst[j++] = '<';
for (i = 0; i < length; i++, j++)
{
dst[j++] = num2char((src[i]>>4) & 0x0f);
dst[j] = num2char(src[i] & 0x0f);
if ((i & 3) == 3 && i < length-1)
{
/* if we've just finished a 32-bit int, print a space */
dst[++j] = ' ';
}
}
dst[j] = '>';
}
}
else if ([obj isKindOfClass: NSDateClass])
{
static NSTimeZone *z = nil;
if (z == nil)
{
z = RETAIN([NSTimeZone timeZoneForSecondsFromGMT: 0]);
}
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<date>" length: 6];
obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%dT%H:%M:%SZ"
timeZone: z locale: nil];
obj = [obj dataUsingEncoding: NSASCIIStringEncoding];
[dest appendData: obj];
[dest appendBytes: "</date>\n" length: 8];
}
else if (x == NSPropertyListGNUstepFormat)
{
[dest appendBytes: "<*D" length: 3];
obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"
timeZone: z locale: nil];
obj = [obj dataUsingEncoding: NSASCIIStringEncoding];
[dest appendData: obj];
[dest appendBytes: ">" length: 1];
}
else
{
obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"
timeZone: z locale: nil];
PString(obj, dest);
}
}
else if ([obj isKindOfClass: NSArrayClass])
{
const char *iBaseString;
const char *iSizeString;
unsigned level = lev;
if (level*step < sizeof(indentStrings)/sizeof(id))
{
iBaseString = indentStrings[level*step];
}
else
{
iBaseString
= indentStrings[sizeof(indentStrings)/sizeof(id)-1];
}
level++;
if (level*step < sizeof(indentStrings)/sizeof(id))
{
iSizeString = indentStrings[level*step];
}
else
{
iSizeString
= indentStrings[sizeof(indentStrings)/sizeof(id)-1];
}
if (x == NSPropertyListXMLFormat_v1_0)
{
NSEnumerator *e;
[dest appendBytes: "<array>\n" length: 8];
e = [obj objectEnumerator];
while ((obj = [e nextObject]))
{
[dest appendBytes: iSizeString length: strlen(iSizeString)];
OAppend(obj, loc, level, step, x, dest);
}
[dest appendBytes: iBaseString length: strlen(iBaseString)];
[dest appendBytes: "</array>\n" length: 9];
}
else
{
unsigned count = [obj count];
unsigned last = count - 1;
NSString *plists[count];
unsigned i;
if ([obj isProxy] == YES)
{
for (i = 0; i < count; i++)
{
plists[i] = [obj objectAtIndex: i];
}
}
else
{
[obj getObjects: plists];
}
if (loc == nil)
{
[dest appendBytes: "(" length: 1];
for (i = 0; i < count; i++)
{
id item = plists[i];
OAppend(item, nil, 0, step, x, dest);
if (i != last)
{
[dest appendBytes: ", " length: 2];
}
}
[dest appendBytes: ")" length: 1];
}
else
{
[dest appendBytes: "(\n" length: 2];
for (i = 0; i < count; i++)
{
id item = plists[i];
[dest appendBytes: iSizeString length: strlen(iSizeString)];
OAppend(item, loc, level, step, x, dest);
if (i == last)
{
[dest appendBytes: "\n" length: 1];
}
else
{
[dest appendBytes: ",\n" length: 2];
}
}
[dest appendBytes: iBaseString length: strlen(iBaseString)];
[dest appendBytes: ")" length: 1];
}
}
}
else if ([obj isKindOfClass: NSDictionaryClass])
{
const char *iBaseString;
const char *iSizeString;
SEL objSel = @selector(objectForKey:);
IMP myObj = [obj methodForSelector: objSel];
unsigned i;
NSArray *keyArray = [obj allKeys];
unsigned numKeys = [keyArray count];
NSString *plists[numKeys];
NSString *keys[numKeys];
BOOL canCompare = YES;
Class lastClass = 0;
unsigned level = lev;
BOOL isProxy = [obj isProxy];
if (level*step < sizeof(indentStrings)/sizeof(id))
{
iBaseString = indentStrings[level*step];
}
else
{
iBaseString
= indentStrings[sizeof(indentStrings)/sizeof(id)-1];
}
level++;
if (level*step < sizeof(indentStrings)/sizeof(id))
{
iSizeString = indentStrings[level*step];
}
else
{
iSizeString
= indentStrings[sizeof(indentStrings)/sizeof(id)-1];
}
if (isProxy == YES)
{
for (i = 0; i < numKeys; i++)
{
keys[i] = [keyArray objectAtIndex: i];
plists[i] = [(NSDictionary*)obj objectForKey: keys[i]];
}
}
else
{
[keyArray getObjects: keys];
for (i = 0; i < numKeys; i++)
{
plists[i] = (*myObj)(obj, objSel, keys[i]);
}
}
if (x == NSPropertyListXMLFormat_v1_0)
{
/* This format can only use strings as keys.
*/
for (i = 0; i < numKeys; i++)
{
if ([keys[i] isKindOfClass: NSStringClass] == NO)
{
[NSException raise: NSInvalidArgumentException
format: @"Bad key (%@) in property list: '%@'",
NSStringFromClass([keys[i] class]), keys[i]];
}
}
}
else if (numKeys == 0)
{
canCompare = NO;
}
else
{
/* All keys must respond to -compare: for sorting.
*/
lastClass = NSStringClass;
for (i = 0; i < numKeys; i++)
{
if (object_getClass(keys[i]) == lastClass)
continue;
if ([keys[i] isKindOfClass: NSStringClass] == NO)
{
canCompare = NO;
break;
}
lastClass = object_getClass(keys[i]);
}
}
if (canCompare == YES)
{
#define STRIDE_FACTOR 3
unsigned c,d, stride;
BOOL found;
NSComparisonResult (*comp)(id, SEL, id) = 0;
unsigned int count = numKeys;
#ifdef GSWARN
BOOL badComparison = NO;
#endif
stride = 1;
while (stride <= count)
{
stride = stride * STRIDE_FACTOR + 1;
}
lastClass = 0;
while (stride > (STRIDE_FACTOR - 1))
{
// loop to sort for each value of stride
stride = stride / STRIDE_FACTOR;
for (c = stride; c < count; c++)
{
found = NO;
if (stride > c)
{
break;
}
d = c - stride;
while (!found)
{
id a = keys[d + stride];
id b = keys[d];
Class x;
NSComparisonResult r;
x = object_getClass(a);
if (x != lastClass)
{
lastClass = x;
comp = (NSComparisonResult (*)(id, SEL, id))
[a methodForSelector: @selector(compare:)];
}
r = (0 == comp) ? 0 : (*comp)(a, @selector(compare:), b);
if (r < 0)
{
#ifdef GSWARN
if (r != NSOrderedAscending)
{
badComparison = YES;
}
#endif
/* Swap keys and values.
*/
keys[d + stride] = b;
keys[d] = a;
a = plists[d + stride];
b = plists[d];
plists[d + stride] = b;
plists[d] = a;
if (stride > d)
{
break;
}
d -= stride;
}
else
{
#ifdef GSWARN
if (r != NSOrderedDescending
&& r != NSOrderedSame)
{
badComparison = YES;
}
#endif
found = YES;
}
}
}
}
#ifdef GSWARN
if (badComparison == YES)
{
NSWarnFLog(@"Detected bad return value from comparison");
}
#endif
}
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<dict>\n" length: 7];
for (i = 0; i < numKeys; i++)
{
[dest appendBytes: iSizeString length: strlen(iSizeString)];
[dest appendBytes: "<key>" length: 5];
XString(keys[i], dest);
[dest appendBytes: "</key>\n" length: 7];
[dest appendBytes: iSizeString length: strlen(iSizeString)];
OAppend(plists[i], loc, level, step, x, dest);
}
[dest appendBytes: iBaseString length: strlen(iBaseString)];
[dest appendBytes: "</dict>\n" length: 8];
}
else if (loc == nil)
{
[dest appendBytes: "{" length: 1];
for (i = 0; i < numKeys; i++)
{
OAppend(keys[i], nil, 0, step, x, dest);
[dest appendBytes: " = " length: 3];
OAppend(plists[i], nil, 0, step, x, dest);
[dest appendBytes: "; " length: 2];
}
[dest appendBytes: "}" length: 1];
}
else
{
[dest appendBytes: "{\n" length: 2];
for (i = 0; i < numKeys; i++)
{
[dest appendBytes: iSizeString length: strlen(iSizeString)];
OAppend(keys[i], loc, level, step, x, dest);
[dest appendBytes: " = " length: 3];
OAppend(plists[i], loc, level, step, x, dest);
[dest appendBytes: ";\n" length: 2];
}
[dest appendBytes: iBaseString length: strlen(iBaseString)];
[dest appendBytes: "}" length: 1];
}
}
else
{
NSString *cls;
if (obj == nil)
{
obj = @"(nil)";
cls = @"(nil)";
}
else
{
cls = NSStringFromClass([obj class]);
}
if (x == NSPropertyListXMLFormat_v1_0)
{
NSDebugLog(@"Non-property-list class (%@) encoded as string", cls);
[dest appendBytes: "<string>" length: 8];
XString([obj description], dest);
[dest appendBytes: "</string>" length: 9];
}
else
{
NSDebugLog(@"Non-property-list class (%@) encoded as string", cls);
PString([obj description], dest);
}
}
}
static inline NSError*
create_error(int code, NSString* desc)
{
return [NSError errorWithDomain: @"NSPropertyListSerialization"
code: code
userInfo: [NSDictionary
dictionaryWithObjectsAndKeys: desc,
NSLocalizedDescriptionKey, nil]];
}
@implementation NSPropertyListSerialization
static BOOL classInitialized = NO;
+ (void) initialize
{
if (classInitialized == NO)
{
classInitialized = YES;
NSStringClass = [NSString class];
NSMutableStringClass = [NSMutableString class];
NSDataClass = [NSData class];
NSDateClass = [NSDate class];
NSNumberClass = [NSNumber class];
NSArrayClass = [NSArray class];
NSDictionaryClass = [NSDictionary class];
GSStringClass = [GSString class];
GSMutableStringClass = [GSMutableString class];
plArray = [GSMutableArray class];
plAdd = (id (*)(id, SEL, id))
[plArray instanceMethodForSelector: @selector(addObject:)];
plDictionary = [GSMutableDictionary class];
plSet = (id (*)(id, SEL, id, id))
[plDictionary instanceMethodForSelector: @selector(setObject:forKey:)];
setupQuotables();
}
}
+ (NSData*) dataFromPropertyList: (id)aPropertyList
format: (NSPropertyListFormat)aFormat
errorDescription: (NSString**)anErrorString
{
NSError *error = nil;
NSData *data = [self dataWithPropertyList: aPropertyList
format: aFormat
options: 0
error: &error];
if ((error != nil) && (anErrorString != NULL))
{
*anErrorString = [error description];
}
return data;
}
+ (NSData *) dataWithPropertyList: (id)aPropertyList
format: (NSPropertyListFormat)aFormat
options: (NSPropertyListWriteOptions)anOption
error: (NSError**)error
{
NSMutableData *dest;
NSDictionary *loc;
int step = 2;
loc = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
dest = [NSMutableData dataWithCapacity: 1024];
if (aFormat == NSPropertyListXMLFormat_v1_0)
{
const char *prefix =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist "
"PUBLIC \"-//GNUstep//DTD plist 0.9//EN\" "
"\"http://www.gnustep.org/plist-0_9.xml\">\n"
"<plist version=\"0.9\">\n";
[dest appendBytes: prefix length: strlen(prefix)];
OAppend(aPropertyList, loc, 0, step > 3 ? 3 : step, aFormat, dest);
[dest appendBytes: "</plist>" length: 8];
}
else if (aFormat == NSPropertyListGNUstepBinaryFormat)
{
[NSSerializer serializePropertyList: aPropertyList intoData: dest];
}
else if (aFormat == NSPropertyListBinaryFormat_v1_0)
{
[GSBinaryPLGenerator serializePropertyList: aPropertyList intoData: dest];
}
else
{
OAppend(aPropertyList, loc, 0, step > 3 ? 3 : step, aFormat, dest);
}
return dest;
}
void
GSPropertyListMake(id obj, NSDictionary *loc, BOOL xml,
BOOL forDescription, unsigned step, id *str)
{
NSString *tmp;
NSPropertyListFormat style;
NSMutableData *dest;
if (classInitialized == NO)
{
[NSPropertyListSerialization class];
}
if (*str == nil)
{
*str = AUTORELEASE([GSMutableString new]);
}
else if (object_getClass(*str) != [GSMutableString class])
{
[NSException raise: NSInvalidArgumentException
format: @"Illegal object (%@) at argument 0", *str];
}
if (forDescription)
{
style = NSPropertyListOpenStepFormat;
}
else if (xml == YES)
{
style = NSPropertyListXMLFormat_v1_0;
}
else if (GSPrivateDefaultsFlag(NSWriteOldStylePropertyLists) == YES)
{
style = NSPropertyListOpenStepFormat;
}
else
{
style = NSPropertyListGNUstepFormat;
}
dest = [NSMutableData dataWithCapacity: 1024];
if (style == NSPropertyListXMLFormat_v1_0)
{
const char *prefix =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist "
"PUBLIC \"-//GNUstep//DTD plist 0.9//EN\" "
"\"http://www.gnustep.org/plist-0_9.xml\">\n"
"<plist version=\"0.9\">\n";
[dest appendBytes: prefix length: strlen(prefix)];
OAppend(obj, loc, 0, step > 3 ? 3 : step, style, dest);
[dest appendBytes: "</plist>" length: 8];
}
else
{
OAppend(obj, loc, 0, step > 3 ? 3 : step, style, dest);
}
tmp = [[NSString alloc] initWithData: dest encoding: NSASCIIStringEncoding];
[*str appendString: tmp];
RELEASE(tmp);
}
+ (BOOL) propertyList: (id)aPropertyList
isValidForFormat: (NSPropertyListFormat)aFormat
{
// FIXME ... need to check properly.
switch (aFormat)
{
case NSPropertyListGNUstepFormat:
return YES;
case NSPropertyListGNUstepBinaryFormat:
return YES;
case NSPropertyListOpenStepFormat:
return YES;
case NSPropertyListXMLFormat_v1_0:
return YES;
case NSPropertyListBinaryFormat_v1_0:
return YES;
default:
[NSException raise: NSInvalidArgumentException
format: @"[%@ +%@]: unsupported format",
NSStringFromClass(self), NSStringFromSelector(_cmd)];
return NO;
}
}
+ (id) propertyListFromData: (NSData*)data
mutabilityOption: (NSPropertyListMutabilityOptions)anOption
format: (NSPropertyListFormat*)aFormat
errorDescription: (NSString**)anErrorString
{
NSError *error = nil;
id prop = [self propertyListWithData: data
options: anOption
format: aFormat
error: &error];
if ((error != nil) && (anErrorString != NULL))
{
*anErrorString = [error description];
}
return prop;
}
+ (id) propertyListWithData: (NSData*)data
options: (NSPropertyListReadOptions)anOption
format: (NSPropertyListFormat*)aFormat
error: (NSError**)error
{
NSPropertyListFormat format = 0;
NSString *errorStr = nil;
id result = nil;
const unsigned char *bytes = 0;
unsigned int length = 0;
if (data == nil)
{
errorStr = @"nil data argument passed to method";
}
else if ([data isKindOfClass: NSDataClass] == NO)
{
errorStr = @"non-NSData data argument passed to method";
}
else if ([data length] == 0)
{
errorStr = @"empty data argument passed to method";
}
else
{
bytes = [data bytes];
length = [data length];
if (length >= 8 && memcmp(bytes, "bplist00", 8) == 0)
{
format = NSPropertyListBinaryFormat_v1_0;
}
else if (bytes[0] == 0 || bytes[0] == 1)
{
format = NSPropertyListGNUstepBinaryFormat;
}
else
{
unsigned int index = 0;
// Skip any leading white space.
while (index < length && GS_IS_WHITESPACE(bytes[index]) == YES)
{
index++;
}
if (length - index > 2
&& bytes[index] == '<' && bytes[index+1] == '?')
{
// It begins with '<?' so it is xml
format = NSPropertyListXMLFormat_v1_0;
}
else
{
// Assume openstep format unless we find otherwise.
format = NSPropertyListOpenStepFormat;
}
}
}
if (errorStr == nil)
{
switch (format)
{
case NSPropertyListXMLFormat_v1_0:
{
GSXMLPListParser *parser;
parser = [GSXMLPListParser alloc];
parser = AUTORELEASE([parser initWithData: data
mutability: anOption]);
if ([parser parse] == YES)
{
result = AUTORELEASE(RETAIN([parser result]));
}
else
{
errorStr = @"failed to parse as XML property list";
}
}
break;
case NSPropertyListOpenStepFormat:
{
pldata _pld;
_pld.ptr = bytes;
_pld.pos = 0;
_pld.end = length;
_pld.err = nil;
_pld.lin = 0;
_pld.opt = anOption;
_pld.key = NO;
_pld.old = YES; // OpenStep style
result = AUTORELEASE(parsePlItem(&_pld));
if (_pld.old == NO)
{
// Found some modern GNUstep extension in data.
format = NSPropertyListGNUstepFormat;
}
if (_pld.err != nil)
{
errorStr = [NSString stringWithFormat:
@"Parse failed at line %d (char %d) - %@",
_pld.lin + 1, _pld.pos + 1, _pld.err];
}
}
break;
case NSPropertyListGNUstepBinaryFormat:
if (anOption == NSPropertyListImmutable)
{
result = [NSDeserializer deserializePropertyListFromData: data
mutableContainers: NO];
}
else
{
result = [NSDeserializer deserializePropertyListFromData: data
mutableContainers: YES];
}
break;
case NSPropertyListBinaryFormat_v1_0:
{
GSBinaryPLParser *p = [GSBinaryPLParser alloc];
p = [p initWithData: data mutability: anOption];
result = [p rootObject];
RELEASE(p);
}
break;
default:
errorStr = @"format not supported";
break;
}
}
/*
* Done ... return all values.
*/
if ((errorStr != nil) && (error != NULL))
{
*error = create_error(0, errorStr);
}
if (aFormat != 0)
{
*aFormat = format;
}
return result;
}
+ (id) propertyListWithStream: (NSInputStream*)stream
options: (NSPropertyListReadOptions)anOption
format: (NSPropertyListFormat*)aFormat
error: (NSError**)error
{
// FIXME
return nil;
}
+ (NSInteger) writePropertyList: (id)aPropertyList
toStream: (NSOutputStream*)stream
format: (NSPropertyListFormat)aFormat
options: (NSPropertyListWriteOptions)anOption
error: (NSError**)error
{
// FIXME: The NSData operations should be implemented on top of this method,
// not the other way round,
NSData *data = [self dataWithPropertyList: aPropertyList
format: aFormat
options: 0
error: error];
return [stream write: [data bytes] maxLength: [data length]];
}
@end
@interface NSPropertyListSerialization (JavaCompatibility)
+ (NSData*) dataFromPropertyList: (id)anObject;
+ (id) propertyListFromData: (NSData*)aData;
+ (id) propertyListFromString: (NSString*)aString;
+ (NSString*) stringFromPropertyList: (id)anObject;
@end
@implementation NSPropertyListSerialization (JavaCompatibility)
+ (NSData*) dataFromPropertyList: (id)anObject
{
NSString *dummy;
if (anObject == nil)
{
return nil;
}
return [self dataFromPropertyList: anObject
format: NSPropertyListGNUstepBinaryFormat
errorDescription: &dummy];
}
+ (id) propertyListFromData: (NSData*)aData
{
NSPropertyListFormat format;
NSString *dummy;
if (aData == nil)
{
return nil;
}
return [self propertyListFromData: aData
mutabilityOption: NSPropertyListImmutable
format: &format
errorDescription: &dummy];
}
+ (id) propertyListFromString: (NSString*)aString
{
NSData *aData;
NSPropertyListFormat format;
NSString *dummy;
aData = [aString dataUsingEncoding: NSUTF8StringEncoding];
if (aData == nil)
{
return nil;
}
return [self propertyListFromData: aData
mutabilityOption: NSPropertyListImmutable
format: &format
errorDescription: &dummy];
}
+ (NSString*) stringFromPropertyList: (id)anObject
{
NSString *string;
NSData *aData;
if (anObject == nil)
{
return nil;
}
aData = [self dataFromPropertyList: anObject
format: NSPropertyListGNUstepFormat
errorDescription: &string];
string = [NSString alloc];
string = [string initWithData: aData encoding: NSASCIIStringEncoding];
return AUTORELEASE(string);
}
@end
@implementation GSBinaryPLParser
- (void) dealloc
{
DESTROY(data);
[super dealloc];
}
- (id) initWithData: (NSData*)plData
mutability: (NSPropertyListMutabilityOptions)m
{
unsigned length;
length = [plData length];
if (length < 32)
{
DESTROY(self);
}
else
{
unsigned char postfix[32];
[plData getBytes: postfix range: NSMakeRange(length - 32, 32)];
offset_size = postfix[6];
index_size = postfix[7];
// FIXME: Looks like the following are actually 8 byte values.
// But taking the lower 4 bytes is currently sufficient.
object_count = (postfix[12] << 24) + (postfix[13] << 16)
+ (postfix[14] << 8) + postfix[15];
root_index = (postfix[20] << 24) + (postfix[21] << 16)
+ (postfix[22] << 8) + postfix[23];
table_start = (postfix[28] << 24) + (postfix[29] << 16)
+ (postfix[30] << 8) + postfix[31];
if (offset_size < 1 || offset_size > 4)
{
unsigned saved = offset_size;
DESTROY(self); // Bad format
[NSException raise: NSGenericException
format: @"Unknown offset size %d", saved];
}
else if (index_size < 1 || index_size > 4)
{
unsigned saved = index_size;
DESTROY(self); // Bad format
[NSException raise: NSGenericException
format: @"Unknown table size %d", saved];
}
else if (root_index >= object_count)
{
DESTROY(self); // Bad format
}
else if (table_start > length - 32)
{
DESTROY(self); // Bad format
}
else
{
ASSIGN(data, plData);
_bytes = (const unsigned char*)[data bytes];
mutability = m;
}
}
return self;
}
- (unsigned long) offsetForIndex: (unsigned)index
{
if (index >= object_count)
{
[NSException raise: NSRangeException
format: @"Object table index out of bounds %d.", index];
}
if (offset_size == 1)
{
unsigned char offset;
[data getBytes: &offset range: NSMakeRange(table_start + index, 1)];
return offset;
}
else if (offset_size == 2)
{
unsigned short offset;
[data getBytes: &offset range: NSMakeRange(table_start + 2*index, 2)];
return NSSwapBigShortToHost(offset);
}
else
{
unsigned char buffer[offset_size];
int i;
unsigned long num = 0;
NSRange r;
r = NSMakeRange(table_start + offset_size*index, offset_size);
[data getBytes: &buffer range: r];
for (i = 0; i < offset_size; i++)
{
num = (num << 8) + buffer[i];
}
return num;
}
return 0;
}
- (unsigned) readObjectIndexAt: (unsigned*)counter
{
if (index_size == 1)
{
unsigned char oid;
[data getBytes: &oid range: NSMakeRange(*counter,1)];
*counter += 1;
return oid;
}
else if (index_size == 2)
{
unsigned short oid;
[data getBytes: &oid range: NSMakeRange(*counter, 2)];
*counter += 2;
return NSSwapBigShortToHost(oid);
}
else
{
unsigned char buffer[index_size];
int i;
unsigned num = 0;
[data getBytes: &buffer range: NSMakeRange(*counter, index_size)];
*counter += index_size;
for (i = 0; i < index_size; i++)
{
num = (num << 8) + buffer[i];
}
return num;
}
return 0;
}
- (unsigned long) readCountAt: (unsigned*) counter
{
unsigned char c;
[data getBytes: &c range: NSMakeRange(*counter,1)];
*counter += 1;
if (c == 0x10)
{
unsigned char count;
[data getBytes: &count range: NSMakeRange(*counter,1)];
*counter += 1;
return count;
}
else if (c == 0x11)
{
unsigned short count;
[data getBytes: &count range: NSMakeRange(*counter,2)];
*counter += 2;
return NSSwapBigShortToHost(count);
}
else if ((c > 0x11) && (c <= 0x13))
{
unsigned len = c - 0x0f;
unsigned char buffer[len];
int i;
unsigned long num = 0;
[data getBytes: &buffer range: NSMakeRange(*counter, len)];
*counter += len;
for (i = 0; i < len; i++)
{
num = (num << 8) + buffer[i];
}
return num;
}
else
{
//FIXME
[NSException raise: NSGenericException
format: @"Unknown count type %d", c];
}
return 0;
}
- (id) rootObject
{
return [self objectAtIndex: root_index];
}
- (id) objectAtIndex: (NSUInteger)index
{
unsigned char next;
unsigned counter = [self offsetForIndex: index];
id result = nil;
[data getBytes: &next range: NSMakeRange(counter,1)];
//NSLog(@"read object %d at index %d type %d", index, counter, next);
counter += 1;
if (next == 0x08)
{
// NO
result = [NSNumber numberWithBool: NO];
}
else if (next == 0x09)
{
// YES
result = [NSNumber numberWithBool: YES];
}
else if ((next >= 0x10) && (next < 0x17))
{
// integer number
unsigned len = 1 << (next - 0x10);
unsigned long long num = 0;
unsigned i;
unsigned char buffer[16];
[data getBytes: buffer range: NSMakeRange(counter, len)];
for (i = 0; i < len; i++)
{
num = (num << 8) + buffer[i];
}
if (next == 0x10)
{
result = [NSNumber numberWithUnsignedChar: (unsigned char)num];
}
else if (next == 0x11)
{
result = [NSNumber numberWithUnsignedShort: (unsigned short)num];
}
else if ((next == 0x12) || (next == 13))
{
result = [NSNumber numberWithUnsignedInt: (unsigned int)num];
}
else
{
result = [NSNumber numberWithUnsignedLongLong: num];
}
}
else if (next == 0x22)
{
// float number
NSSwappedFloat in;
[data getBytes: &in range: NSMakeRange(counter, sizeof(float))];
result = [NSNumber numberWithFloat: NSSwapBigFloatToHost(in)];
}
else if (next == 0x23)
{
// double number
NSSwappedDouble in;
[data getBytes: &in range: NSMakeRange(counter, sizeof(double))];
result = [NSNumber numberWithDouble: NSSwapBigDoubleToHost(in)];
}
else if (next == 0x33)
{
NSSwappedDouble in;
// Date
NSDate *date;
[data getBytes: &in range: NSMakeRange(counter, sizeof(double))];
date = [NSDate dateWithTimeIntervalSinceReferenceDate:
NSSwapBigDoubleToHost(in)];
result = date;
}
else if ((next >= 0x40) && (next < 0x4F))
{
// short data
unsigned len = next - 0x40;
if (mutability == NSPropertyListMutableContainersAndLeaves)
{
result = [NSMutableData dataWithBytes: _bytes + counter
length: len];
}
else
{
result = [data subdataWithRange: NSMakeRange(counter, len)];
}
}
else if (next == 0x4F)
{
// long data
unsigned long len;
len = [self readCountAt: &counter];
if (mutability == NSPropertyListMutableContainersAndLeaves)
{
result = [NSMutableData dataWithBytes: _bytes + counter
length: len];
}
else
{
result = [data subdataWithRange: NSMakeRange(counter, len)];
}
}
else if ((next >= 0x50) && (next < 0x5F))
{
// Short string
unsigned len = next - 0x50;
char buffer[len+1];
[data getBytes: buffer range: NSMakeRange(counter, len)];
buffer[len] = '\0';
if (mutability == NSPropertyListMutableContainersAndLeaves)
{
result = [NSMutableString stringWithUTF8String: buffer];
}
else
{
result = [NSString stringWithUTF8String: buffer];
}
}
else if (next == 0x5F)
{
// long string
unsigned long len;
char *buffer;
len = [self readCountAt: &counter];
buffer = NSAllocateCollectable(len + 1, 0);
[data getBytes: buffer range: NSMakeRange(counter, len)];
buffer[len] = '\0';
if (mutability == NSPropertyListMutableContainersAndLeaves)
{
result = [NSMutableString stringWithUTF8String: buffer];
}
else
{
result = [NSString stringWithUTF8String: buffer];
}
NSZoneFree(NSDefaultMallocZone(), buffer);
}
else if ((next >= 0x60) && (next < 0x6F))
{
// Short unicode string
unsigned len = next - 0x60;
unsigned i;
unichar buffer[len];
[data getBytes: buffer
range: NSMakeRange(counter, sizeof(unichar)*len)];
for (i = 0; i < len; i++)
{
buffer[i] = NSSwapBigShortToHost(buffer[i]);
}
if (mutability == NSPropertyListMutableContainersAndLeaves)
{
result = [NSMutableString stringWithCharacters: buffer length: len];
}
else
{
result = [NSString stringWithCharacters: buffer length: len];
}
}
else if (next == 0x6F)
{
// long unicode string
unsigned long len;
unsigned i;
unichar *buffer;
len = [self readCountAt: &counter];
buffer = NSAllocateCollectable(sizeof(unichar) * len, 0);
[data getBytes: buffer range: NSMakeRange(counter, sizeof(unichar)*len)];
for (i = 0; i < len; i++)
{
buffer[i] = NSSwapBigShortToHost(buffer[i]);
}
if (mutability == NSPropertyListMutableContainersAndLeaves)
{
result = [NSMutableString stringWithCharacters: buffer length: len];
}
else
{
result = [NSString stringWithCharacters: buffer length: len];
}
NSZoneFree(NSDefaultMallocZone(), buffer);
}
else if (next == 0x80)
{
unsigned char index;
[data getBytes: &index range: NSMakeRange(counter,1)];
result = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt: index]
forKey: @"CF$UID"];
}
else if (next == 0x81)
{
unsigned short index;
[data getBytes: &index range: NSMakeRange(counter,2)];
index = NSSwapBigShortToHost(index);
result = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt: index]
forKey: @"CF$UID"];
}
else if ((next >= 0xA0) && (next < 0xAF))
{
// short array
unsigned len = next - 0xA0;
unsigned i;
id objects[len];
for (i = 0; i < len; i++)
{
int oid = [self readObjectIndexAt: &counter];
objects[i] = [self objectAtIndex: oid];
}
if (mutability == NSPropertyListMutableContainersAndLeaves
|| mutability == NSPropertyListMutableContainers)
{
result = [NSMutableArray arrayWithObjects: objects count: len];
}
else
{
result = [NSArray arrayWithObjects: objects count: len];
}
}
else if (next == 0xAF)
{
// big array
unsigned long len;
unsigned i;
id *objects;
len = [self readCountAt: &counter];
objects = NSAllocateCollectable(sizeof(id) * len, NSScannedOption);
for (i = 0; i < len; i++)
{
int oid = [self readObjectIndexAt: &counter];
objects[i] = [self objectAtIndex: oid];
}
if (mutability == NSPropertyListMutableContainersAndLeaves
|| mutability == NSPropertyListMutableContainers)
{
result =[NSMutableArray arrayWithObjects: objects count: len];
}
else
{
result =[NSArray arrayWithObjects: objects count: len];
}
NSZoneFree(NSDefaultMallocZone(), objects);
}
else if ((next >= 0xD0) && (next < 0xDF))
{
// dictionary
unsigned len = next - 0xD0;
unsigned i;
id keys[len];
id values[len];
for (i = 0; i < len; i++)
{
int oid = [self readObjectIndexAt: &counter];
keys[i] = [self objectAtIndex: oid];
}
for (i = 0; i < len; i++)
{
int oid = [self readObjectIndexAt: &counter];
values[i] = [self objectAtIndex: oid];
}
if (mutability == NSPropertyListMutableContainersAndLeaves
|| mutability == NSPropertyListMutableContainers)
{
result = [NSMutableDictionary dictionaryWithObjects: values
forKeys: keys
count: len];
}
else
{
result = [NSDictionary dictionaryWithObjects: values
forKeys: keys
count: len];
}
}
else if (next == 0xDF)
{
// big dictionary
unsigned long len;
unsigned i;
id *keys;
id *values;
len = [self readCountAt: &counter];
keys = NSAllocateCollectable(sizeof(id) * len * 2, NSScannedOption);
values = keys + len;
for (i = 0; i < len; i++)
{
int oid = [self readObjectIndexAt: &counter];
keys[i] = [self objectAtIndex: oid];
}
for (i = 0; i < len; i++)
{
int oid = [self readObjectIndexAt: &counter];
values[i] = [self objectAtIndex: oid];
}
if (mutability == NSPropertyListMutableContainersAndLeaves
|| mutability == NSPropertyListMutableContainers)
{
result = [NSMutableDictionary dictionaryWithObjects: values
forKeys: keys
count: len];
}
else
{
result = [NSDictionary dictionaryWithObjects: values
forKeys: keys
count: len];
}
NSZoneFree(NSDefaultMallocZone(), keys);
}
else
{
[NSException raise: NSGenericException
format: @"Unknown control byte = %d", next];
}
return result;
}
@end
/* Test two items for equality ... boith are objects.
* If either is an NSNumber, we insist that they are the same class
* so that numbers with the same numeric value but different classes
* are not treated as the same number (that confuses OSXs decoding).
*/
static BOOL
isEqualFunc(const void *item1, const void *item2,
NSUInteger (*size)(const void *item))
{
id o1 = (id)item1;
id o2 = (id)item2;
if ([o1 isKindOfClass: [NSNumber class]]
|| [o2 isKindOfClass: [NSNumber class]])
{
if ([o1 class] != [o2 class])
{
return NO;
}
}
return [o1 isEqual: o2];
}
@implementation GSBinaryPLGenerator
+ (void) serializePropertyList: (id)aPropertyList
intoData: (NSMutableData *)destination
{
GSBinaryPLGenerator *gen;
gen = [[GSBinaryPLGenerator alloc]
initWithPropertyList: aPropertyList intoData: destination];
[gen generate];
RELEASE(gen);
}
- (id) initWithPropertyList: (id) aPropertyList
intoData: (NSMutableData *)destination
{
ASSIGN(root, aPropertyList);
ASSIGN(dest, destination);
[dest setLength: 0];
return self;
}
- (void) dealloc
{
DESTROY(root);
[self cleanup];
DESTROY(dest);
[super dealloc];
}
- (NSData*) data
{
return dest;
}
- (void) setup
{
NSPointerFunctions *k;
NSPointerFunctions *v;
[dest setLength: 0];
if (index_size == 1)
{
table_size = 256;
}
else if (index_size == 2)
{
table_size = 256 * 256;
}
else if (index_size == 3)
{
table_size = 256 * 256 * 256;
}
else if (index_size == 4)
{
table_size = UINT_MAX;
}
table = NSZoneMalloc(0, table_size * sizeof(int));
objectsToDoList = [[NSMutableArray alloc] init];
k = [NSPointerFunctions pointerFunctionsWithOptions:
NSPointerFunctionsObjectPersonality];
[k setIsEqualFunction: isEqualFunc];
v = [NSPointerFunctions pointerFunctionsWithOptions:
NSPointerFunctionsIntegerPersonality|NSPointerFunctionsOpaqueMemory];
objectList = [[NSMapTable alloc] initWithKeyPointerFunctions: k
valuePointerFunctions: v
capacity: 1000];
[objectsToDoList addObject: root];
[objectList setObject: (id)1 forKey: root];
}
- (void) cleanup
{
DESTROY(objectsToDoList);
DESTROY(objectList);
if (table != NULL)
{
NSZoneFree(0, table);
table = NULL;
}
}
- (void) writeObjects
{
id object;
const char *prefix = "bplist00";
[dest appendBytes: prefix length: strlen(prefix)];
while ([objectsToDoList count] != 0)
{
object = [objectsToDoList objectAtIndex: 0];
[self storeObject: object];
[objectsToDoList removeObjectAtIndex: 0];
}
}
- (void) markOffset: (unsigned int) offset for: (id)object
{
int oid;
oid = (NSInteger)[objectList objectForKey: object];
if (oid <= 0)
{
[NSException raise: NSGenericException
format: @"Unknown object %@.", object];
}
oid--;
if (oid >= table_size)
{
[NSException raise: NSRangeException
format: @"Object table index out of bounds %d.", oid];
}
table[oid] = offset;
}
- (void) writeObjectTable
{
unsigned int size;
unsigned int len;
unsigned int i;
unsigned char *buffer;
unsigned int last_offset;
table_start = [dest length];
// This is a bit too much, as the length
// of the last object is added.
last_offset = table_start;
if (last_offset < 256)
{
offset_size = 1;
}
else if (last_offset < 256 * 256)
{
offset_size = 2;
}
else if (last_offset < 256 * 256 * 256)
{
offset_size = 3;
}
else if (last_offset <= UINT_MAX)
{
offset_size = 4;
}
else
{
[NSException raise: NSRangeException
format: @"Object table offset out of bounds %d.", last_offset];
}
len = [objectList count];
size = offset_size * len;
buffer = NSZoneMalloc(0, size);
if (offset_size == 1)
{
for (i = 0; i < len; i++)
{
unsigned char ci;
ci = table[i];
buffer[i] = ci;
}
}
else if (offset_size == 2)
{
for (i = 0; i < len; i++)
{
unsigned short si;
si = table[i];
buffer[2 * i] = (si >> 8);
buffer[2 * i + 1] = si % 256;
}
}
else if (offset_size == 3)
{
for (i = 0; i < len; i++)
{
unsigned int si;
si = table[i];
buffer[3 * i] = (si >> 16);
buffer[3 * i + 1] = (si >> 8) % 256;
buffer[3 * i + 2] = si % 256;
}
}
else if (offset_size == 4)
{
for (i = 0; i < len; i++)
{
unsigned int si;
si = table[i];
buffer[4 * i] = (si >> 24);
buffer[4 * i + 1] = (si >> 16) % 256;
buffer[4 * i + 2] = (si >> 8) % 256;
buffer[4 * i + 3] = si % 256;
}
}
[dest appendBytes: buffer length: size];
NSZoneFree(0, buffer);
}
- (void) writeMetaData
{
unsigned char meta[32];
unsigned int i;
unsigned int len;
for (i = 0; i < 32; i++)
{
meta[i] = 0;
}
meta[6] = offset_size;
meta[7] = index_size;
len = [objectList count];
meta[12] = (len >> 24);
meta[13] = (len >> 16) % 256;
meta[14] = (len >> 8) % 256;
meta[15] = len % 256;
// root index is always 0, no need to write it
meta[28] = (table_start >> 24);
meta[29] = (table_start >> 16) % 256;
meta[30] = (table_start >> 8) % 256;
meta[31] = table_start % 256;
[dest appendBytes: meta length: 32];
}
- (NSInteger) indexForObject: (id)object
{
NSInteger index;
index = (NSInteger)[objectList objectForKey: object];
if (index <= 0)
{
index = [objectList count];
[objectList setObject: (id)(++index) forKey: object];
[objectsToDoList addObject: object];
}
return index - 1;
}
- (void) storeIndex: (NSInteger)index
{
if (index_size == 1)
{
unsigned char oid;
oid = index;
[dest appendBytes: &oid length: 1];
}
else if (index_size == 2)
{
unsigned short oid;
oid = NSSwapHostShortToBig(index);
[dest appendBytes: &oid length: 2];
}
else if (index_size == 3)
{
unsigned char buffer[index_size];
int i;
unsigned num = index;
for (i = index_size - 1; i >= 0; i--)
{
buffer[i] = num & 0xFF;
num >>= 8;
}
[dest appendBytes: buffer length: index_size];
}
else if (index_size == 4)
{
unsigned int oid;
oid = NSSwapHostIntToBig(index);
[dest appendBytes: &oid length: 4];
}
else
{
[NSException raise: NSGenericException
format: @"Unknown table size %d", index_size];
}
}
- (void) storeCount: (unsigned int)count
{
unsigned char code;
if (count < 256)
{
unsigned char c;
code = 0x10;
[dest appendBytes: &code length: 1];
c = count;
[dest appendBytes: &c length: 1];
}
else if (count < 256 * 256)
{
unsigned short c;
code = 0x11;
[dest appendBytes: &code length: 1];
c = count;
c = NSSwapHostShortToBig(c);
[dest appendBytes: &c length: 2];
}
else
{
code = 0x13;
[dest appendBytes: &code length: 1];
count = NSSwapHostIntToBig(count);
[dest appendBytes: &count length: 4];
}
}
- (void) storeData: (NSData*) data
{
unsigned int len;
unsigned char code;
len = [data length];
if (len < 0x0F)
{
code = 0x40 + len;
[dest appendBytes: &code length: 1];
[dest appendData: data];
}
else
{
code = 0x4F;
[dest appendBytes: &code length: 1];
[self storeCount: len];
[dest appendData: data];
}
}
- (void) storeString: (NSString*) string
{
unsigned int len;
BOOL ascii = YES;
unsigned char code;
unsigned int i;
unichar uchar;
len = [string length];
for (i = 0; i < len; i++)
{
uchar = [string characterAtIndex: i];
if (uchar > 127)
{
ascii = NO;
break;
}
}
if (ascii)
{
if (len < 0x0F)
{
code = 0x50 + len;
[dest appendBytes: &code length: 1];
[dest appendBytes: [string cString] length: len];
}
else
{
code = 0x5F;
[dest appendBytes: &code length: 1];
[self storeCount: len];
[dest appendBytes: [string cString] length: len];
}
}
else
{
if (len < 0x0F)
{
unichar buffer[len + 1];
int i;
code = 0x60 + len;
[dest appendBytes: &code length: 1];
[string getCharacters: buffer];
for (i = 0; i < len; i++)
{
buffer[i] = NSSwapHostShortToBig(buffer[i]);
}
[dest appendBytes: buffer length: len * sizeof(unichar)];
}
else
{
unichar *buffer;
code = 0x6F;
[dest appendBytes: &code length: 1];
buffer = NSZoneMalloc(0, sizeof(unichar)*(len + 1));
[self storeCount: len];
[string getCharacters: buffer];
for (i = 0; i < len; i++)
{
buffer[i] = NSSwapHostShortToBig(buffer[i]);
}
[dest appendBytes: buffer length: sizeof(unichar)*len];
NSZoneFree(0, buffer);
}
}
}
- (void) storeNumber: (NSNumber*) number
{
const char *type;
unsigned char code;
type = [number objCType];
switch (*type)
{
case 'c':
case 'C':
case 's':
case 'S':
case 'i':
case 'I':
case 'l':
case 'L':
case 'q':
case 'Q':
{
unsigned long long val;
val = [number unsignedLongLongValue];
// FIXME: We need a better way to determine boolean values!
if ((val == 0) && ((*type == 'c') || (*type == 'C')))
{
code = 0x08;
[dest appendBytes: &code length: 1];
}
else if ((val == 1) && ((*type == 'c') || (*type == 'C')))
{
code = 0x09;
[dest appendBytes: &code length: 1];
}
else if (val < 256)
{
unsigned char cval;
code = 0x10;
[dest appendBytes: &code length: 1];
cval = (unsigned char) val;
[dest appendBytes: &cval length: 1];
}
else if (val < 256 * 256)
{
unsigned short sval;
code = 0x11;
[dest appendBytes: &code length: 1];
sval = NSSwapHostShortToBig([number unsignedShortValue]);
[dest appendBytes: &sval length: 2];
}
else if (val <= UINT_MAX)
{
unsigned int ival;
code = 0x12;
[dest appendBytes: &code length: 1];
ival = NSSwapHostIntToBig([number unsignedIntValue]);
[dest appendBytes: &ival length: 4];
}
else
{
unsigned long long lval;
code = 0x13;
[dest appendBytes: &code length: 1];
lval = NSSwapHostLongLongToBig([number unsignedLongLongValue]);
[dest appendBytes: &lval length: 8];
}
break;
}
case 'f':
{
NSSwappedFloat val = NSSwapHostFloatToBig([number floatValue]);
code = 0x22;
[dest appendBytes: &code length: 1];
[dest appendBytes: &val length: sizeof(float)];
break;
}
case 'd':
{
NSSwappedDouble val = NSSwapHostDoubleToBig([number doubleValue]);
code = 0x23;
[dest appendBytes: &code length: 1];
[dest appendBytes: &val length: sizeof(double)];
break;
}
default:
[NSException raise: NSGenericException
format: @"Attempt to store number with unknown ObjC type"];
}
}
- (void) storeDate: (NSDate*) date
{
unsigned char code;
NSSwappedDouble out;
code = 0x33;
[dest appendBytes: &code length: 1];
out = NSSwapHostDoubleToBig([date timeIntervalSinceReferenceDate]);
[dest appendBytes: &out length: sizeof(double)];
}
- (void) storeArray: (NSArray*) array
{
unsigned char code;
unsigned int len;
unsigned int i;
len = [array count];
if (len < 0x0F)
{
code = 0xA0 + len;
[dest appendBytes: &code length: 1];
}
else
{
code = 0xAF;
[dest appendBytes: &code length: 1];
[self storeCount: len];
}
for (i = 0; i < len; i++)
{
id obj;
NSInteger oid;
obj = [array objectAtIndex: i];
oid = [self indexForObject: obj];
[self storeIndex: oid];
}
}
- (void) storeDictionary: (NSDictionary*) dict
{
unsigned char code;
NSNumber *num;
unsigned int i;
num = [dict objectForKey: @"CF$UID"];
if (num != nil)
{
// Special dictionary from keyed encoding
unsigned int index;
index = [num intValue];
if (index < 256)
{
unsigned char ci;
code = 0x80;
[dest appendBytes: &code length: 1];
ci = (unsigned char)index;
[dest appendBytes: &ci length: 1];
}
else
{
unsigned short si;
code = 0x81;
[dest appendBytes: &code length: 1];
si = NSSwapHostShortToBig((unsigned short)index);
[dest appendBytes: &si length: 2];
}
}
else
{
unsigned int len = [dict count];
NSArray *keys = [dict allKeys];
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: len];
id key;
for (i = 0; i < len; i++)
{
key = [keys objectAtIndex: i];
[objects addObject: [dict objectForKey: key]];
}
if (len < 0x0F)
{
code = 0xD0 + len;
[dest appendBytes: &code length: 1];
}
else
{
code = 0xDF;
[dest appendBytes: &code length: 1];
[self storeCount: len];
}
for (i = 0; i < len; i++)
{
id obj;
NSInteger oid;
obj = [keys objectAtIndex: i];
oid = [self indexForObject: obj];
[self storeIndex: oid];
}
for (i = 0; i < len; i++)
{
id obj;
NSInteger oid;
obj = [objects objectAtIndex: i];
oid = [self indexForObject: obj];
[self storeIndex: oid];
}
}
}
- (void) storeObject: (id)object
{
[self markOffset: [dest length] for: object];
if ([object isKindOfClass: NSStringClass])
{
[self storeString: object];
}
else if ([object isKindOfClass: NSDataClass])
{
[self storeData: object];
}
else if ([object isKindOfClass: NSNumberClass])
{
[self storeNumber: object];
}
else if ([object isKindOfClass: NSDateClass])
{
[self storeDate: object];
}
else if ([object isKindOfClass: NSArrayClass])
{
[self storeArray: object];
}
else if ([object isKindOfClass: NSDictionaryClass])
{
[self storeDictionary: object];
}
else
{
NSLog(@"Unknown object class %@", object);
}
}
- (void) generate
{
BOOL done = NO;
index_size = 1;
while (!done && (index_size <= 4))
{
NS_DURING
{
[self setup];
[self writeObjects];
done = YES;
}
NS_HANDLER
{
[self cleanup];
index_size += 1;
}
NS_ENDHANDLER
}
[self writeObjectTable];
[self writeMetaData];
}
@end