libs-base/Source/NSPropertyList.m
David Ayers a5cf839255 * Source/NSPropertyList.m (OAppend): Revert patch from
2005-02-28.  It seems to fix the issue in the wrong place.
        Further investigation is necessary.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@21208 72102866-910b-0410-8b05-ffd578937521
2005-05-09 16:36:54 +00:00

3665 lines
78 KiB
Objective-C
Raw Permalink 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 Library 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 Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include "config.h"
#include <string.h>
#include "GNUstepBase/preface.h"
#include "GNUstepBase/GSMime.h"
#include "Foundation/NSArray.h"
#include "Foundation/NSAutoreleasePool.h"
#include "Foundation/NSByteOrder.h"
#include "Foundation/NSCalendarDate.h"
#include "Foundation/NSCharacterSet.h"
#include "Foundation/NSData.h"
#include "Foundation/NSDictionary.h"
#include "Foundation/NSException.h"
#include "Foundation/NSPropertyList.h"
#include "Foundation/NSSerialization.h"
#include "Foundation/NSString.h"
#include "Foundation/NSTimeZone.h"
#include "Foundation/NSUserDefaults.h"
#include "Foundation/NSValue.h"
#include "Foundation/NSDebug.h"
#include "GNUstepBase/Unicode.h"
#include "GSPrivate.h"
extern BOOL GSScanDouble(unichar*, unsigned, double*);
#ifndef UINT_MAX
# define UINT_MAX 4294967295U
#endif
@class GSString;
@class GSMutableString;
@class GSMutableArray;
@class GSMutableDictionary;
@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 table_start; // Start address of object table
unsigned table_len; // Length of object table
}
- (id) initWithData: (NSData*)plData
mutability: (NSPropertyListMutabilityOptions)m;
- (id) rootObject;
- (id) objectAtIndex: (unsigned)index;
@end
@interface BinaryPLGenerator : NSObject
{
NSMutableData *dest;
NSMutableArray *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
/*
* Cache classes and method implementations for speed.
*/
static Class NSDataClass;
static Class NSStringClass;
static Class NSMutableStringClass;
static Class GSStringClass;
static Class GSMutableStringClass;
static Class plArray;
static id (*plAdd)(id, SEL, id) = 0;
static Class plDictionary;
static id (*plSet)(id, SEL, id, id) = 0;
#define IS_BIT_SET(a,i) ((((a) & (1<<(i)))) > 0)
static unsigned const char *hexdigitsBitmapRep = NULL;
#define GS_IS_HEXDIGIT(X) IS_BIT_SET(hexdigitsBitmapRep[(X)/8], (X) % 8)
static void setupHexdigits(void)
{
if (hexdigitsBitmapRep == NULL)
{
NSCharacterSet *hexdigits;
NSData *bitmap;
hexdigits = [NSCharacterSet characterSetWithCharactersInString:
@"0123456789abcdefABCDEF"];
bitmap = RETAIN([hexdigits bitmapRepresentation]);
hexdigitsBitmapRep = [bitmap bytes];
}
}
static NSCharacterSet *quotables = nil;
static NSCharacterSet *oldQuotables = nil;
static NSCharacterSet *xmlQuotables = nil;
static unsigned const char *quotablesBitmapRep = NULL;
#define GS_IS_QUOTABLE(X) IS_BIT_SET(quotablesBitmapRep[(X)/8], (X) % 8)
static void setupQuotables(void)
{
if (quotablesBitmapRep == NULL)
{
NSMutableCharacterSet *s;
NSData *bitmap;
s = [[NSCharacterSet characterSetWithCharactersInString:
@"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@"abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^"]
mutableCopy];
[s invert];
quotables = [s copy];
RELEASE(s);
bitmap = RETAIN([quotables bitmapRepresentation]);
quotablesBitmapRep = [bitmap bytes];
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);
}
}
static unsigned const char *whitespaceBitmapRep = NULL;
#define GS_IS_WHITESPACE(X) IS_BIT_SET(whitespaceBitmapRep[(X)/8], (X) % 8)
static void setupWhitespace(void)
{
if (whitespaceBitmapRep == NULL)
{
NSCharacterSet *whitespace;
NSData *bitmap;
/*
We can not use whitespaceAndNewlineCharacterSet here as this would lead
to a recursion, as this also reads in a property list.
whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
*/
whitespace = [NSCharacterSet characterSetWithCharactersInString:
@" \t\r\n\f\b"];
bitmap = RETAIN([whitespace bitmapRepresentation]);
whitespaceBitmapRep = [bitmap bytes];
}
}
#ifdef HAVE_LIBXML
#include "GNUstepBase/GSXML.h"
static int XML_ELEMENT_NODE;
#endif
#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))
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 && GS_IS_HEXDIGIT(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 = NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * length);
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 && GS_IS_HEXDIGIT(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 = NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * length);
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] != '}')
{
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--;
if (type == 'I')
{
char buf[len+1];
for (i = 0; i < len; i++) buf[i] = (char)ptr[i];
buf[len] = '\0';
result = [[NSNumber alloc] initWithLong: atol(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
&& GS_IS_HEXDIGIT(pld->ptr[pld->pos])
&& GS_IS_HEXDIGIT(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;
}
#ifdef HAVE_LIBXML
static GSXMLNode*
elementNode(GSXMLNode* node)
{
while (node != nil)
{
if ([node type] == XML_ELEMENT_NODE)
{
break;
}
node = [node next];
}
return node;
}
static id
nodeToObject(GSXMLNode* node, NSPropertyListMutabilityOptions o, NSString **e)
{
CREATE_AUTORELEASE_POOL(arp);
id result = nil;
node = elementNode(node);
if (node != nil)
{
NSString *name;
NSString *content;
GSXMLNode *children;
BOOL isKey = NO;
name = [node name];
children = [node firstChild];
content = [children content];
children = elementNode(children);
isKey = [name isEqualToString: @"key"];
if (isKey == YES || [name isEqualToString: @"string"] == YES)
{
if (content == nil)
{
content = @"";
}
else
{
NSRange r;
r = [content rangeOfString: @"\\"];
if (r.length == 1)
{
unsigned len = [content length];
unichar buf[len];
unsigned pos = r.location;
[content getCharacters: buf];
while (pos < len)
{
if (++pos < len)
{
if ((buf[pos] == 'u' || buf[pos] == 'U')
&& (len >= pos + 4))
{
unichar val = 0;
unsigned i;
BOOL ok = YES;
for (i = 1; i < 5; i++)
{
unichar c = buf[pos + i];
if (c >= '0' && c <= '9')
{
val = (val << 4) + c - '0';
}
else if (c >= 'A' && c <= 'F')
{
val = (val << 4) + c - 'A' + 10;
}
else if (c >= 'a' && c <= 'f')
{
val = (val << 4) + c - 'a' + 10;
}
else
{
ok = NO;
}
}
if (ok == YES)
{
len -= 5;
memcpy(&buf[pos], &buf[pos+5],
(len - pos) * sizeof(unichar));
buf[pos - 1] = val;
}
}
while (pos < len && buf[pos] != '\\')
{
pos++;
}
}
}
if (isKey == NO
&& o == NSPropertyListMutableContainersAndLeaves)
{
content = [NSMutableString stringWithCharacters: buf
length: len];
}
else
{
content = [NSString stringWithCharacters: buf
length: len];
}
}
}
result = content;
}
else if ([name isEqualToString: @"true"])
{
result = [NSNumber numberWithBool: YES];
}
else if ([name isEqualToString: @"false"])
{
result = [NSNumber numberWithBool: NO];
}
else if ([name isEqualToString: @"integer"])
{
if (content == nil)
{
content = @"0";
}
result = [NSNumber numberWithInt: [content intValue]];
}
else if ([name isEqualToString: @"real"])
{
if (content == nil)
{
content = @"0.0";
}
result = [NSNumber numberWithDouble: [content doubleValue]];
}
else if ([name isEqualToString: @"date"])
{
if (content == nil)
{
content = @"";
}
if ([content hasSuffix: @"Z"] == YES && [content length] == 20)
{
result = [NSCalendarDate dateWithString: content
calendarFormat: @"%Y-%m-%dT%H:%M:%SZ"];
}
else
{
result = [NSCalendarDate dateWithString: content
calendarFormat: @"%Y-%m-%d %H:%M:%S %z"];
}
}
else if ([name isEqualToString: @"data"])
{
result = [GSMimeDocument decodeBase64:
[content dataUsingEncoding: NSASCIIStringEncoding]];
if (o == NSPropertyListMutableContainersAndLeaves)
{
result = AUTORELEASE([result mutableCopy]);
}
}
// container class
else if ([name isEqualToString: @"array"])
{
NSMutableArray *container = [plArray array];
while (children != nil)
{
id val;
val = nodeToObject(children, o, e);
[container addObject: val];
children = [children nextElement];
}
result = container;
if (o == NSPropertyListImmutable)
{
[result makeImmutableCopyOnFail: NO];
}
}
else if ([name isEqualToString: @"dict"])
{
NSMutableDictionary *container = [plDictionary dictionary];
while (children != nil)
{
NSString *key;
id val;
key = nodeToObject(children, o, e);
children = [children nextElement];
val = nodeToObject(children, o, e);
children = [children nextElement];
[container setObject: val forKey: key];
}
result = container;
if (o == NSPropertyListImmutable)
{
[result makeImmutableCopyOnFail: NO];
}
}
}
RETAIN(result);
RELEASE(arp);
return AUTORELEASE(result);
}
#endif
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 tmp[length <= 1024 ? length : 0];
unichar *ustring;
unichar *from;
unichar *end;
unsigned char *ptr;
int base = [output length];
int len = 0;
if (length <= 1024)
{
ustring = tmp;
}
else
{
ustring = NSZoneMalloc(NSDefaultMallocZone(), length*sizeof(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++ = '"';
if (ustring != tmp)
{
NSZoneFree(NSDefaultMallocZone(), ustring);
}
}
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 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;
base = NSZoneMalloc(NSDefaultMallocZone(), end * sizeof(unichar));
[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)
{
len += 6;
}
else
{
len++;
}
break;
}
}
map = NSZoneMalloc(NSDefaultMallocZone(), len * sizeof(unichar));
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)
{
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];
[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 ([obj isKindOfClass: [NSString class]])
{
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: [NSNumber class]])
{
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>\n" length: 6];
}
else
{
PString([obj description], dest);
}
}
else
{
if (x == NSPropertyListXMLFormat_v1_0)
{
[dest appendBytes: "<false/>\n" length: 9];
}
else if (x == NSPropertyListGNUstepFormat)
{
[dest appendBytes: "<*BN>\n" length: 6];
}
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];
PString([obj stringValue], dest);
[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];
PString([obj stringValue], dest);
[dest appendBytes: ">" length: 1];
}
else
{
PString([obj description], dest);
}
}
}
else if ([obj isKindOfClass: [NSData class]])
{
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: [NSDate class]])
{
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: [NSArray class]])
{
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: [NSDictionary class]])
{
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];
}
}
else
{
[keyArray getObjects: keys];
}
for (i = 0; i < numKeys; i++)
{
if (GSObjCClass(keys[i]) == lastClass)
continue;
if ([keys[i] respondsToSelector: @selector(compare:)] == NO)
{
canCompare = NO;
break;
}
lastClass = GSObjCClass(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 = GSObjCClass(a);
if (x != lastClass)
{
lastClass = x;
comp = (NSComparisonResult (*)(id, SEL, id))
[a methodForSelector: @selector(compare:)];
}
r = (*comp)(a, @selector(compare:), b);
if (r < 0)
{
#ifdef GSWARN
if (r != NSOrderedAscending)
{
badComparison = YES;
}
#endif
keys[d + stride] = b;
keys[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 (isProxy == YES)
{
for (i = 0; i < numKeys; i++)
{
plists[i] = [(NSDictionary*)obj objectForKey: keys[i]];
}
}
else
{
for (i = 0; i < numKeys; i++)
{
plists[i] = (*myObj)(obj, objSel, keys[i]);
}
}
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);
}
}
}
@implementation NSPropertyListSerialization
static BOOL classInitialized = NO;
+ (void) initialize
{
if (classInitialized == NO)
{
classInitialized = YES;
#ifdef HAVE_LIBXML
/*
* Cache XML node information.
*/
XML_ELEMENT_NODE = [GSXMLNode typeFromDescription: @"XML_ELEMENT_NODE"];
#endif
NSStringClass = [NSString class];
NSMutableStringClass = [NSMutableString class];
NSDataClass = [NSData 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:)];
setupHexdigits();
setupQuotables();
setupWhitespace();
}
}
+ (NSData*) dataFromPropertyList: (id)aPropertyList
format: (NSPropertyListFormat)aFormat
errorDescription: (NSString**)anErrorString
{
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)
{
[BinaryPLGenerator 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 (GSObjCClass(*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 (GSUserDefaultsFlag(NSWriteOldStylePropertyLists))
{
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
{
NSPropertyListFormat format = 0;
NSString *error = nil;
id result = nil;
const unsigned char *bytes = 0;
unsigned int length = 0;
if (data == nil)
{
error = @"nil data argument passed to method";
}
else if ([data isKindOfClass: [NSData class]] == NO)
{
error = @"non-NSData data argument passed to method";
}
else if ([data length] == 0)
{
error = @"empty data argument passed to method";
}
else
{
bytes = [data bytes];
length = [data length];
if (length > 0 && 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;
#ifndef HAVE_LIBXML
error = @"XML format not supported ... XML support not present.";
#endif
}
else
{
// Assume openstep format unless we find otherwise.
format = NSPropertyListOpenStepFormat;
}
}
}
if (error == nil)
{
switch (format)
{
#ifdef HAVE_LIBXML
case NSPropertyListXMLFormat_v1_0:
{
GSXMLParser *parser;
GSXMLNode *node;
parser = [GSXMLParser parser];
[parser substituteEntities: YES];
[parser doValidityChecking: YES];
if ([parser parse: data] == NO || [parser parse: nil] == NO)
{
error = @"failed to parse as valid XML matching DTD";
}
node = [[parser document] root];
if (error == nil && [[node name] isEqualToString: @"plist"] == NO)
{
error = @"failed to parse as XML property list";
}
if (error == nil)
{
result = nodeToObject([node firstChild], anOption, &error);
}
}
break;
#endif
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)
{
error = [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:
error = @"format not supported";
break;
}
}
/*
* Done ... return all values.
*/
if (anErrorString != 0)
{
*anErrorString = error;
}
if (aFormat != 0)
{
*aFormat = format;
}
return result;
}
@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];
// FIXME: Get more of the details
[plData getBytes: postfix range: NSMakeRange(length-32, 32)];
offset_size = postfix[6];
index_size = postfix[7];
table_start = (postfix[28] << 24) + (postfix[29] << 16) + (postfix[30] << 8) + postfix[31];
if (offset_size < 1 || offset_size > 4)
{
[NSException raise: NSGenericException
format: @"Unknown table size %d", offset_size];
DESTROY(self); // Bad format
}
else if (index_size < 1 || index_size > 4)
{
[NSException raise: NSGenericException
format: @"Unknown table size %d", index_size];
DESTROY(self); // Bad format
}
else if (table_start > length - 32)
{
DESTROY(self); // Bad format
}
else
{
table_len = length - table_start - 32;
ASSIGN(data, plData);
_bytes = (const unsigned char*)[data bytes];
mutability = m;
}
}
return self;
}
- (unsigned long) offsetForIndex: (unsigned)index
{
if (index > table_len)
{
[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;
[data getBytes: &buffer range: NSMakeRange(table_start + offset_size*index, offset_size)];
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 = 1 << (c - 0x10);
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: 0];
}
- (id) objectAtIndex: (unsigned)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)
{
double 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;
unsigned 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 = NSZoneMalloc(NSDefaultMallocZone(), len+1);
[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 = NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar)*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];
}
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 = NSZoneMalloc(NSDefaultMallocZone(), sizeof(id) * 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];
}
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 = NSZoneMalloc(NSDefaultMallocZone(), sizeof(id)*len);
values = NSZoneMalloc(NSDefaultMallocZone(), sizeof(id)*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(), values);
NSZoneFree(NSDefaultMallocZone(), keys);
}
else
{
[NSException raise: NSGenericException
format: @"Unknown control byte = %d", next];
}
return result;
}
@end
@implementation BinaryPLGenerator
+ (void) serializePropertyList: (id)aPropertyList intoData: (NSMutableData *)destination
{
BinaryPLGenerator *gen;
gen = [[BinaryPLGenerator alloc] initWithPropertyList: aPropertyList intoData: destination];
[gen generate];
RELEASE(gen);
}
- (id) initWithPropertyList: (id) aPropertyList intoData: (NSMutableData *)destination
{
ASSIGN(root, aPropertyList);
ASSIGN(dest, destination);
return self;
}
- (void) dealloc
{
RELEASE(root);
[self cleanup];
[super dealloc];
}
- (NSData*) data
{
return dest;
}
- (void) setup
{
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 = malloc(table_size * sizeof(int));
// Always restart the destination data
[dest setLength: 0];
objectsToDoList = [[NSMutableArray alloc] init];
objectList = [[NSMutableArray alloc] init];
[objectsToDoList addObject: root];
[objectList addObject: root];
}
- (void) cleanup
{
DESTROY(dest);
DESTROY(objectsToDoList);
DESTROY(objectList);
if (table != NULL)
{
free(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
{
unsigned int oid;
oid = [objectList indexOfObject: object];
if (oid == NSNotFound)
{
[NSException raise: NSGenericException
format: @"Unknown object %@.", object];
}
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 to 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 = malloc(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];
free(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;
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];
}
- (unsigned int) indexForObject: (id)object
{
unsigned int index;
index = [objectList indexOfObject: object];
if (index == NSNotFound)
{
index = [objectList count];
[objectList addObject: object];
[objectsToDoList addObject: object];
}
return index;
}
- (void) storeIndex: (unsigned int)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 == 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
{
unsigned short c;
code = 0x11;
[dest appendBytes: &code length: 1];
c = count;
NSSwapHostShortToBig(c);
[dest appendBytes: &c length: 2];
}
}
- (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 = malloc(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];
free(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;
double 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;
unsigned int 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;
unsigned int oid;
obj = [keys objectAtIndex: i];
oid = [self indexForObject: obj];
[self storeIndex: oid];
}
for (i = 0; i < len; i++)
{
id obj;
unsigned int 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: [NSString class]])
{
[self storeString: object];
}
else if ([object isKindOfClass: [NSData class]])
{
[self storeData: object];
}
else if ([object isKindOfClass: [NSNumber class]])
{
[self storeNumber: object];
}
else if ([object isKindOfClass: [NSDate class]])
{
[self storeDate: object];
}
else if ([object isKindOfClass: [NSArray class]])
{
[self storeArray: object];
}
else if ([object isKindOfClass: [NSDictionary class]])
{
[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