diff --git a/ChangeLog b/ChangeLog
index 74f42c0be..4a509c156 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2004-01-19 Richard Frith-Macdonald
+
+ * Headers/Foundation/NSPropertyList.h: serialisation/deserialisation.
+ * Source/NSPropertyList.m: serialisation/deserialisation code partly
+ implemented.
+ * Headers/Foundation/NSCoder.h: Add keyed coding methods.
+ * Source/NSCoder.m: ditto
+ * Headers/Foundation/NSKeyedArchiver.h: new keyed archiver/unarchiver.
+ * Source/NSKeyedArchiver.m: New MacOS-X compatible class implementation.
+ * Source/NSKeyedUnarchiver.m: Stub for new class implementation.
+ * Source/GNUmakefile: build new keyed archiving stuff.
+ * Source/GSCompatibility.m: fix indentation error in xml output.
+ improve output of numeric values in xml format.
+ * Source/Additions/GSMime.m: ([-setContentType:) make argument more
+ strongly typed. ([-encodeBase64:])tweak buffer size.
+
2004-01-18 Richard Frith-Macdonald
* Source/NSDistantObject.m: All code conditional on USE_FFCALL should
diff --git a/Headers/Foundation/NSCoder.h b/Headers/Foundation/NSCoder.h
index 3a804da36..0f50fc232 100644
--- a/Headers/Foundation/NSCoder.h
+++ b/Headers/Foundation/NSCoder.h
@@ -77,6 +77,137 @@
- (unsigned int) systemVersion;
- (unsigned int) versionForClassName: (NSString*)className;
+#ifndef STRICT_OPENSTEP
+/*
+ * MacOS-X adds some typedefs that GNUstep already has by another name.
+ */
+#include
+#define uint8_t gsu8
+#define int32_t gss32
+#define int64_t gss64
+
+
+/**
+ * Returns a flag indicating whether the receiver supported keyed coding.
+ * the default implementation returns NO. Subclasses supporting keyed
+ * coding must override this to return YES.
+ */
+- (BOOL) allowsKeyedCoding;
+
+/**
+ * Returns a class indicating whether an encoded value corresponding
+ * to aKey exists.
+ */
+- (BOOL) containsValueForKey: (NSString*)aKey;
+
+/**
+ * Returns a boolean value associated with aKey. This value must previously
+ * have been encoded using -encodeBool:forKey:
+ */
+- (BOOL) decodeBoolForKey: (NSString*)aKey;
+
+/**
+ * Returns a pointer to a byte array associated with aKey.
+ * Returns the length of the data in aLength.
+ * This value must previously have been encoded using
+ * -encodeBytes:length:forKey:
+ */
+- (const uint8_t*) decodeBytesForKey: (NSString*)aKey
+ returnedLength: (unsigned*)alength;
+
+/**
+ * Returns a double value associated with aKey. This value must previously
+ * have been encoded using -encodeDouble:forKey: or -encodeFloat:forKey:
+ */
+- (double) decodeDoubleForKey: (NSString*)aKey;
+
+/**
+ * Returns a float value associated with aKey. This value must previously
+ * have been encoded using -encodeFloat:forKey: or -encodeDouble:forKey:
+ * Precision may be lost (or an exception raised if the value will not fit
+ * in a float) if the value was encoded using -encodeDouble:forKey:,
+ */
+- (float) decodeFloatForKey: (NSString*)aKey;
+
+/**
+ * Returns an integer value associated with aKey. This value must previously
+ * have been encoded using -encodeInt:forKey:, -encodeInt32:forKey:, or
+ * -encodeInt64:forKey:.
+ * An exception will be raised if the value does not fit in an integer.
+ */
+- (int) decodeIntForKey: (NSString*)aKey;
+
+/**
+ * Returns a 32-bit integer value associated with aKey. This value must
+ * previously have been encoded using -encodeInt:forKey:,
+ * -encodeInt32:forKey:, or -encodeInt64:forKey:.
+ * An exception will be raised if the value does not fit in a 32-bit integer.
+ */
+- (int32_t) decodeInt32ForKey: (NSString*)aKey;
+
+/**
+ * Returns a 64-bit integer value associated with aKey. This value must
+ * previously have been encoded using -encodeInt:forKey:,
+ * -encodeInt32:forKey:, or -encodeInt64:forKey:.
+ */
+- (int64_t) decodeInt64ForKey: (NSString*)aKey;
+
+/**
+ * Returns an object value associated with aKey. This value must
+ * previously have been encoded using -encodeObject:forKey: or
+ * -encodeConditionalObject:forKey:
+ */
+- (id) decodeObjectForKey: (NSString*)aKey;
+
+/**
+ * Encodes aBool and associates the encoded value with aKey.
+ */
+- (void) encodeBool: (BOOL) aBool forKey: (NSString*)aKey;
+
+/**
+ * Encodes the data of the specified length and pointed to by aPointeraBool,
+ * and associates the encoded value with aKey.
+ */
+- (void) encodeBytes: (const uint8_t*)aPointer
+ length: (unsigned)length
+ forKey: (NSString*)aKey;
+
+/**
+ * Encodes anObject and associates the encoded value with aKey, but only
+ * if anObject has already been encoded using -encodeObject:forKey:
+ */
+- (void) encodeConditionalObject: (id)anObject forKey: (NSString*)aKey;
+
+/**
+ * Encodes aDouble and associates the encoded value with aKey.
+ */
+- (void) encodeDouble: (double)aDouble forKey: (NSString*)aKey;
+
+/**
+ * Encodes aFloat and associates the encoded value with aKey.
+ */
+- (void) encodeFloat: (float)aFloat forKey: (NSString*)aKey;
+
+/**
+ * Encodes anInteger and associates the encoded value with aKey.
+ */
+- (void) encodeInt: (int)anInteger forKey: (NSString*)aKey;
+
+/**
+ * Encodes anInteger and associates the encoded value with aKey.
+ */
+- (void) encodeInt32: (int32_t)anInteger forKey: (NSString*)aKey;
+
+/**
+ * Encodes anInteger and associates the encoded value with aKey.
+ */
+- (void) encodeInt64: (int64_t)anInteger forKey: (NSString*)aKey;
+
+/**
+ * Encodes anObject and associates the encoded value with aKey.
+ */
+- (void) encodeObject: (id)anObject forKey: (NSString*)aKey;
+#endif
@end
#ifndef NO_GNUSTEP
diff --git a/Headers/Foundation/NSKeyedArchiver.h b/Headers/Foundation/NSKeyedArchiver.h
new file mode 100644
index 000000000..47e0c46da
--- /dev/null
+++ b/Headers/Foundation/NSKeyedArchiver.h
@@ -0,0 +1,353 @@
+/** Interface for NSKeyedArchiver for GNUStep
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+ Written by: Richard Frith-Macdonald
+ Date: January 2004
+
+ 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.
+
+ AutogsdocSource: NSKeyedArchiver.m
+ AutogsdocSource: NSKeyedUnarchiver.m
+
+ */
+
+#ifndef __NSKeyedArchiver_h_GNUSTEP_BASE_INCLUDE
+#define __NSKeyedArchiver_h_GNUSTEP_BASE_INCLUDE
+
+#ifndef STRICT_OPENSTEP
+
+#include
+#include
+#include
+
+@class NSMutableDictionary, NSMutableData, NSData, NSString;
+
+/**
+ * Keyed archiving class, NOT YET IMPLEMENTED
+ */
+@interface NSKeyedArchiver : NSCoder
+{
+@private
+ NSMutableData *_data; /* Data to write into. */
+ id _delegate; /* Delegate controls operation. */
+ NSMapTable *_clsMap; /* Map classes to names. */
+#ifndef _IN_NSKEYEDARCHIVER_M
+#define GSIMapTable void*
+#endif
+ GSIMapTable _cIdMap; /* Conditionally coded. */
+ GSIMapTable _uIdMap; /* Unconditionally coded. */
+ GSIMapTable _repMap; /* Mappings for objects. */
+#ifndef _IN_NSKEYEDARCHIVER_M
+#undef GSIMapTable
+#endif
+ unsigned _keyNum; /* Counter for keys in object. */
+ NSMutableDictionary *_enc; /* Object being encoded. */
+ NSMutableArray *_obj; /* Array of objects. */
+ NSPropertyListFormat _format;
+}
+
+/**
+ * Encodes anObject and returns the resulting data object.
+ */
++ (NSData*) archivedDataWithRootObject: (id)anObject;
+
+/**
+ * Encodes anObject and writes the resulting data ti aPath.
+ */
++ (BOOL) archiveRootObject: (id)anObject toFile: (NSString*)aPath;
+
+/**
+ * Returns the class name with which the NSKeyedArchiver class will encode
+ * instances of aClass, or nil if no name mapping has been set using the
+ * +setClassName:forClass: method.
+ */
++ (NSString*) classNameForClass: (Class)aClass;
+
+/**
+ * Sets the class name with which the NSKeyedArchiver class will encode
+ * instances of aClass. This mapping is used only if no class name
+ * mapping has been set for the individual instance of NSKeyedArchiver
+ * being used.
+ * The value of aString must be the name of an existing class.
+ * If the value of aString is nil, any mapping for aClass is removed.
+ */
++ (void) setClassName: (NSString*)aString forClass: (Class)aClass;
+
+/**
+ * Returns any mapping for the name of aClass which was previously set
+ * for the receiver using the -setClassName:forClass: method.
+ * Returns nil if no such mapping exists, even if one has been set
+ * using the class method +setClassName:forClass:
+ */
+- (NSString*) classNameForClass: (Class)aClass;
+
+/**
+ * Returns the delegate set for the receiver, or nil of none is set.
+ */
+- (id) delegate;
+
+- (void) encodeBool: (BOOL)aBool forKey: (NSString*)aKey;
+- (void) encodeBytes: (const uint8_t*)aPointer length: (unsigned)length forKey: (NSString*)aKey;
+- (void) encodeConditionalObject: (id)anObject forKey: (NSString*)aKey;
+- (void) encodeDouble: (double)aDouble forKey: (NSString*)aKey;
+- (void) encodeFloat: (float)aFloat forKey: (NSString*)aKey;
+- (void) encodeInt: (int)anInteger forKey: (NSString*)aKey;
+- (void) encodeInt32: (int32_t)anInteger forKey: (NSString*)aKey;
+- (void) encodeInt64: (int64_t)anInteger forKey: (NSString*)aKey;
+- (void) encodeObject: (id)anObject forKey: (NSString*)aKey;
+
+/**
+ * Ends the encoding process and causes the encoded archive to be placed
+ * in the mutable data object supplied when the receiver was initialised.
+ * This method must be called at the end of encoding, and nothing may be
+ * encoded after this method is called.
+ */
+- (void) finishEncoding;
+
+/**
+ * Initialise the receiver to encode an archive into the supplied
+ * data object.
+ */
+- (id) initForWritingWithMutableData: (NSMutableData*)data;
+
+/**
+ * Returns the output format of the archived data ... this should default
+ * to the MacOS-X binary format, but we don't support that yet, so the
+ * -setOutputFormat: method should be used to set a supported format.
+ */
+- (NSPropertyListFormat) outputFormat;
+
+/**
+ * Sets the name with which instances of aClass are encoded.
+ * The value of aString must be the anme of an existing clas.
+ */
+- (void) setClassName: (NSString*)aString forClass: (Class)aClass;
+
+/**
+ * Sets the receivers delegate. The delegate should conform to the
+ * NSObject(NSKeyedArchiverDelegate) informal protocol.
+ * NB. the delegate is not retained, so you must ensure that it is not
+ * deallocated before the archiver has finished with it.
+ */
+- (void) setDelegate: (id)anObject;
+
+/**
+ * Specifies the output format of the archived data ... this should default
+ * to the MacOS-X binary format, but we don't support that yet, so the
+ * -setOutputFormat: method should be used to set a supported format.
+ */
+- (void) setOutputFormat: (NSPropertyListFormat)format;
+
+@end
+
+
+
+/**
+ * Keyed unarchiving class, NOT YET IMPLEMENTED
+ */
+@interface NSKeyedUnarchiver : NSCoder
+{
+@private
+ id _delegate;
+#ifndef _IN_NSKEYEDUNARCHIVER_M
+#define GSIArray void*
+#endif
+ GSIArray clsMap; /* Class crossreference map. */
+ GSIArray objMap; /* Object crossreference map. */
+ GSIArray ptrMap; /* Pointer crossreference map. */
+#ifndef _IN_NSKEYEDUNARCHIVER_M
+#undef GSUnarchiverArray
+#endif
+ unsigned cursor; /* Position in data buffer. */
+ NSZone *zone; /* Zone for allocating objs. */
+ NSMutableDictionary *objDict; /* Class information store. */
+}
+
++ (Class) classForClassName: (NSString*)aString;
++ (void) setClass: (Class)aClass forClassName: (NSString*)aString;
++ (id) unarchiveObjectWithData: (NSData*)data;
++ (id) unarchiveObjectWithFile: (NSString*)aPath;
+
+- (Class) classForClassName: (NSString*)aString;
+- (BOOL) containsValueForKey: (NSString*)aKey;
+- (BOOL) decodeBoolForKey: (NSString*)aKey;
+- (const uint8_t*) decodeBytesForKey: (NSString*)aKey
+ returnedLength: (unsigned*)length;
+- (double) decodeDoubleForKey: (NSString*)aKey;
+- (float) decodeFloatForKey: (NSString*)aKey;
+- (int) decodeIntForKey: (NSString*)aKey;
+- (int32_t) decodeInt32ForKey: (NSString*)aKey;
+- (int64_t) decodeInt64ForKey: (NSString*)aKey;
+- (id) decodeObjectForKey: (NSString*)aKey;
+/**
+ * returns the delegate of the unarchiver.
+ */
+- (id) delegate;
+- (void) finishDecoding;
+- (id) initForReadingWithData: (NSData*)data;
+- (void) setClass: (Class)aClass forClassName: (NSString*)aString;
+/**
+ * Sets the receivers delegate. The delegate should conform to the
+ * NSObject(NSKeyedUnarchiverDelegate) informal protocol.
+ * NB. the delegate is not retained, so you must ensure that it is not
+ * deallocated before the unarchiver has finished with it.
+ */
+- (void) setDelegate: (id)delegate;
+
+@end
+
+
+/* Exceptions */
+GS_EXPORT NSString * const NSInvalidArchiveOperationException;
+GS_EXPORT NSString * const NSInvalidUnarchiveOperationException;
+
+
+/**
+ * Informal protocol implemented by delegates of [NSKeyedArchiver]
+ */
+@interface NSObject (NSKeyedArchiverDelegate)
+
+/**
+ * Sent when encoding of anObject has completed except in the case
+ * of conditional encoding.
+ */
+- (void) archiver: (NSKeyedArchiver*)anArchiver didEncodeObject: (id)anObject;
+
+/**
+ * Sent when anObject is about to be encoded (or conditionally encoded)
+ * and provides the receiver with an opportunity to change the actual
+ * object stored into the archive by returning a different value (otherwise
+ * it should return anObject).
+ * The method is not called for encoding of nil or for encoding of any
+ * object for which has already been called.
+ * The method is called after the -replacementObjectForKeyedArchiver:
+ * method.
+ */
+- (id) archiver: (NSKeyedArchiver*)anArchiver willEncodeObject: (id)anObject;
+
+/**
+ * Sent when the encoding process is complete.
+ */
+- (void) archiverDidFinish: (NSKeyedArchiver*)anArchiver;
+
+/**
+ * Sent when the encoding process is about to finish.
+ */
+- (void) archiverWillFinish: (NSKeyedArchiver*)anArchiver;
+
+/**
+ * Sent whenever object replacement occurs during encoding, either by the
+ * -replacementObjectForKeyedArchiver: method or because the delegate has
+ * returned a changed value using the -archiver:willEncodeObject: method.
+ */
+- (void) archiver: (NSKeyedArchiver*)anArchiver
+willReplaceObject: (id)anObject
+ withObject: (id)newObject;
+
+@end
+
+
+
+/**
+ * Informal protocol implemented by delegates of [NSKeyedUnarchiver]
+ */
+@interface NSObject (NSKeyedUnarchiverDelegate)
+
+/**
+ * Sent if the named class is not available during decoding.
+ * The value of aName is the class name being decoded (after any name mapping
+ * has been applied).
+ * The classNames arraay contains the original name of the class encoded
+ * in the archive, and is followed by eqach of its superclasses in turn.
+ * The delegate may either return a class object for the unarchiver to use
+ * to continue decoding, or may return nil to abort the decoding process.
+ */
+- (Class) unarchiver: (NSKeyedUnarchiver*)anUnarchiver
+ cannotDecodeObjectOfClassName: (NSString*)aName
+ originalClasses: (NSArray*)classNames;
+
+/**
+ * Sent when anObject is decoded. The receiver may return either anObject
+ * or some other object (including nil). If a value other than anObject is
+ * returned, it is used to replace anObject.
+ */
+- (id) unarchiver: (NSKeyedUnarchiver*)anUnarchiver
+ didDecodeObject: (id)anObject;
+
+/**
+ * Sent when unarchiving is about to complete.
+ */
+- (void) unarchiverDidFinish: (NSKeyedUnarchiver*)anUnarchiver;
+
+/**
+ * Sent when unarchiving has been completed.
+ */
+- (void) unarchiverWillFinish: (NSKeyedUnarchiver*)anUnarchiver;
+
+/**
+ * Sent whenever object replacement occurs during decoding, eg by the
+ * -replacementObjectForKeyedArchiver: method.
+ */
+- (void) unarchiver: (NSKeyedUnarchiver*)anUnarchiver
+ willReplaceObject: (id)anObject
+ withObject: (id)newObject;
+
+@end
+
+
+
+/**
+ * Methods by which a class may control its archiving by the NSKeyedArchiver
+ */
+@interface NSObject (NSKeyedArchiverObjectSubstitution)
+
+/**
+ * This message is sent to objects being encoded, to allow them to choose
+ * to be encoded a different class. If this returns nil it is treated as
+ * if it returned the class of the object.
+ * After this method is applied, any class name mapping set in the archiver
+ * is applied to its result.
+ * The default implementation returns the result of the -classForArchiver
+ * method.
+ */
+- (Class) classForKeyedArchiver;
+
+/**
+ * This message is sent to objects being encoded, to allow them to choose
+ * to be encoded a different object by returning the alternative object.
+ * The default implementation returns the result of calling
+ * the -replacementObjectForArchiver: method with a nil argument.
+ * This is called only if no mapping has been set up in the archiver already.
+ */
+- (id) replacementObjectForKeyedArchiver: (NSKeyedArchiver*)archiver;
+
+@end
+
+@interface NSObject (NSKeyedUnarchiverObjectSubstitution)
+
+/**
+ * Sent during unarchiving to permit classes to substitute a different
+ * class for decoded instances of themselves.
+ * Default implementation returns the receiver.
+ * Overrides the mappings set up within the receiver.
+ */
++ (Class) classForKeyedUnarchiver;
+
+@end
+
+#endif /* STRICT_OPENSTEP */
+#endif /* __NSKeyedArchiver_h_GNUSTEP_BASE_INCLUDE*/
diff --git a/Headers/Foundation/NSPropertyList.h b/Headers/Foundation/NSPropertyList.h
new file mode 100644
index 000000000..97ee414e1
--- /dev/null
+++ b/Headers/Foundation/NSPropertyList.h
@@ -0,0 +1,125 @@
+/** Interface for NSPropertyList for GNUStep
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+ Written by: Richard Frith-Macdonald
+ Date: January 2004
+
+ 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.
+
+ AutogsdocSource: NSPropertyList.m
+
+ */
+
+#ifndef __NSPropertyList_h_GNUSTEP_BASE_INCLUDE
+#define __NSPropertyList_h_GNUSTEP_BASE_INCLUDE
+
+#ifndef STRICT_OPENSTEP
+
+#include
+
+@class NSData, NSString;
+
+/**
+ * Describes the mutability to use when generating objects during
+ * deserialisation of a property list.
+ */
+typedef enum {
+ NSPropertyListImmutable,
+/** NSPropertyListImmutable
+ * all objects in created list are immutable
+ */
+ NSPropertyListMutableContainers,
+/** NSPropertyListMutableContainers
+ * dictionaries and arrays are mutable
+ */
+ NSPropertyListMutableContainersAndLeaves
+/** NSPropertyListMutableContainersAndLeaves
+ * dictionaries, arrays, strings and data objects are mutable
+ */
+} NSPropertyListMutabilityOptions;
+
+/**
+ * Specifies the serialisation format for a serialised property list.
+ */
+typedef enum {
+ NSPropertyListGNUStepFormat,
+/** NSPropertyListGNUStepFormat
+ * extension of OpenStep format */
+ NSPropertyListGNUstepBinaryFormat,
+/** NSPropertyListGNUstepBinaryFormat
+ * efficient, hardware independent */
+ NSPropertyListOpenStepFormat,
+/** NSPropertyListOpenStepFormat
+ * the most human-readable format */
+ NSPropertyListXMLFormat_v1_0,
+/** NSPropertyListXMLFormat_v1_0
+ * portable and readable */
+ NSPropertyListBinaryFormat_v1_0,
+/** NSPropertyListBinaryFormat_v1_0
+ * not yet supported */
+} NSPropertyListFormat;
+
+/**
+ * The NSPropertyListSerialization class provides facilities for
+ * serialising and deserializing property list data in a number of
+ * formats.
+ *
+ * You do not work with instances of this class, instead you use a
+ * small number of claass methods to serialized and deserialize
+ * property lists.
+ *
+ */
+@interface NSPropertyListSerialization : NSObject
+{
+}
+
+/**
+ * Creates and returns a data object containing a serialized representation
+ * of plist. The argument aFormat is used to determine the way in which the
+ * data is serialised, and the anErrorString argument is a pointer in which
+ * an error message is returned on failure (nil is returned on success).
+ */
++ (NSData*) dataFromPropertyList: (id)aPropertyList
+ format: (NSPropertyListFormat)aFormat
+ errorDescription: (NSString**)anErrorString;
+
+/**
+ * Returns a flag indicating whether it is possible to serialize aPropertyList
+ * in the format aFormat.
+ */
++ (BOOL) propertyList: (id)aPropertyList
+ isValidForFormat: (NSPropertyListFormat)aFormat;
+
+/**
+ * Deserialises dataItem and returns the resulting property list
+ * (or nil if the data does not contain a property list serialised
+ * in a supported format).
+ * The argument anOption is ised to control whether the objects making
+ * up the deserialized property list are mutable or not.
+ * The argument aFormat is either null or a pointer to a location
+ * in which the format of the serialized property list will be returned.
+ * Either nil or an error message will be returned in anErrorString.
+ */
++ (id) propertyListFromData: (NSData*)data
+ mutabilityOption: (NSPropertyListMutabilityOptions)anOption
+ format: (NSPropertyListFormat*)aFormat
+ errorDescription: (NSString**)anErrorString;
+
+@end
+
+#endif /* STRICT_OPENSTEP */
+#endif /* __NSPropertyList_h_GNUSTEP_BASE_INCLUDE*/
diff --git a/Source/Additions/GSMime.m b/Source/Additions/GSMime.m
index b1229e049..bbe637f61 100644
--- a/Source/Additions/GSMime.m
+++ b/Source/Additions/GSMime.m
@@ -108,7 +108,6 @@ encodebase64(char *dst, const unsigned char *src, int length)
dst[dIndex - 1] = '=';
dst[dIndex - 2] = '=';
}
- dst[dIndex] = '\0';
return dIndex;
}
@@ -3155,10 +3154,9 @@ static NSCharacterSet *tokenSet = nil;
{
return [NSData data];
}
- destlen = 4 * ((length - 1) / 3) + 5;
+ destlen = 4 * ((length + 2) / 3);
sBuf = (unsigned char*)[source bytes];
dBuf = NSZoneMalloc(NSDefaultMallocZone(), destlen);
- dBuf[destlen - 1] = '\0';
destlen = encodebase64(dBuf, sBuf, length);
@@ -4539,7 +4537,7 @@ static NSCharacterSet *tokenSet = nil;
* and parameters as found after the colon in a mime Content-Type header.
*
*/
-- (void) setContentType: (id)newType
+- (void) setContentType: (NSString *)newType
{
CREATE_AUTORELEASE_POOL(arp);
GSMimeHeader *hdr = nil;
diff --git a/Source/DocMakefile b/Source/DocMakefile
index 6c6225d1c..18fc75eff 100644
--- a/Source/DocMakefile
+++ b/Source/DocMakefile
@@ -64,6 +64,7 @@ NSGeometry.h \
NSHashTable.h \
NSHost.h \
NSInvocation.h \
+NSKeyedArchiver.h \
NSKeyValueCoding.h \
NSLock.h \
NSMapTable.h \
@@ -79,6 +80,7 @@ NSPort.h \
NSPortCoder.h \
NSPortMessage.h \
NSPortNameServer.h \
+NSPropertyList.h \
NSProcessInfo.h \
NSProtocolChecker.h \
NSProxy.h \
diff --git a/Source/GNUmakefile b/Source/GNUmakefile
index 2596365a2..d18e45efc 100644
--- a/Source/GNUmakefile
+++ b/Source/GNUmakefile
@@ -178,6 +178,8 @@ NSGeometry.m \
NSHashTable.m \
NSHost.m \
NSInvocation.m \
+NSKeyedArchiver.m \
+NSKeyedUnarchiver.m \
NSKeyValueCoding.m \
NSLock.m \
NSLog.m \
@@ -198,6 +200,7 @@ NSPortCoder.m \
NSPortMessage.m \
NSPortNameServer.m \
NSProcessInfo.m \
+NSPropertyList.m \
NSProtocolChecker.m \
NSProxy.m \
NSRange.m \
@@ -291,6 +294,7 @@ NSGeometry.h \
NSHashTable.h \
NSHost.h \
NSInvocation.h \
+NSKeyedArchiver.h \
NSKeyValueCoding.h \
NSLock.h \
NSMapTable.h \
@@ -307,6 +311,7 @@ NSPortCoder.h \
NSPortMessage.h \
NSPortNameServer.h \
NSProcessInfo.h \
+NSPropertyList.h \
NSProtocolChecker.h \
NSProxy.h \
NSRange.h \
diff --git a/Source/GSCompatibility.m b/Source/GSCompatibility.m
index 50b101f52..202cde7c2 100644
--- a/Source/GSCompatibility.m
+++ b/Source/GSCompatibility.m
@@ -436,39 +436,44 @@ OAppend(id obj, NSDictionary *loc, unsigned lev, unsigned step,
}
else if ([obj isKindOfClass: [NSNumber class]])
{
- double val = [obj doubleValue];
+ const char *t = [obj objCType];
- if (val == 1.0)
+ if (*t == 'c' || *t == 'C')
{
- if (x == PLXML)
+ BOOL val = [obj boolValue];
+
+ if (val == YES)
{
- Append(@"\n", dest);
- }
- else if (x == PLNEW)
- {
- Append(@"<*BY>", dest);
+ if (x == PLXML)
+ {
+ Append(@"\n", dest);
+ }
+ else if (x == PLNEW)
+ {
+ Append(@"<*BY>", dest);
+ }
+ else
+ {
+ PString([obj description], dest);
+ }
}
else
{
- PString([obj description], dest);
+ if (x == PLXML)
+ {
+ Append(@"\n", dest);
+ }
+ else if (x == PLNEW)
+ {
+ Append(@"<*BN>", dest);
+ }
+ else
+ {
+ PString([obj description], dest);
+ }
}
}
- else if (val == 0.0)
- {
- if (x == PLXML)
- {
- Append(@"\n", dest);
- }
- else if (x == PLNEW)
- {
- Append(@"<*BN>", dest);
- }
- else
- {
- PString([obj description], dest);
- }
- }
- else if (rint(val) == val)
+ else if (strchr("sSiIlLqQ", *t) != 0)
{
if (x == PLXML)
{
@@ -601,7 +606,7 @@ OAppend(id obj, NSDictionary *loc, unsigned lev, unsigned step,
{
NSEnumerator *e;
- Append(@"", dest);
+ Append(@"\n", dest);
e = [obj objectEnumerator];
while ((obj = [e nextObject]))
{
@@ -700,6 +705,7 @@ OAppend(id obj, NSDictionary *loc, unsigned lev, unsigned step,
Append(@"", dest);
XString(key, dest);
Append(@"\n", dest);
+ Append(iSizeString, dest);
OAppend(val, loc, level, step, x, dest);
}
Append(iBaseString, dest);
diff --git a/Source/NSCoder.m b/Source/NSCoder.m
index 54d0e8c27..f28f73755 100644
--- a/Source/NSCoder.m
+++ b/Source/NSCoder.m
@@ -294,6 +294,116 @@
+ GNUSTEP_BASE_MINOR_VERSION) * 100) + GNUSTEP_BASE_SUBMINOR_VERSION;
}
+
+// Keyed archiving extensions
+
+- (BOOL) allowsKeyedCoding
+{
+ return NO;
+}
+
+- (BOOL) containsValueForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return NO;
+}
+
+- (BOOL) decodeBoolForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return NO;
+}
+
+- (const uint8_t*) decodeBytesForKey: (NSString*)aKey
+ returnedLength: (unsigned*)alength
+{
+ [self subclassResponsibility: _cmd];
+ return 0;
+}
+
+- (double) decodeDoubleForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return 0.0;
+}
+
+- (float) decodeFloatForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return 0.0;
+}
+
+- (int) decodeIntForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return 0;
+}
+
+- (int32_t) decodeInt32ForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return 0;
+}
+
+- (int64_t) decodeInt64ForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return 0;
+}
+
+- (id) decodeObjectForKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+ return nil;
+}
+
+- (void) encodeBool: (BOOL) aBool forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeBytes: (const uint8_t*)aPointer
+ length: (unsigned)length
+ forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeConditionalObject: (id)anObject forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeDouble: (double)aDouble forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeFloat: (float)aFloat forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeInt: (int)anInteger forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeInt32: (int32_t)anInteger forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeInt64: (int64_t)anInteger forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
+- (void) encodeObject: (id)anObject forKey: (NSString*)aKey
+{
+ [self subclassResponsibility: _cmd];
+}
+
@end
@implementation NSCoder (GNUstep)
@@ -378,4 +488,6 @@
{
[self encodeValueOfObjCType: @encode(id) at: anObject withName: name];
}
+
+
@end
diff --git a/Source/NSKeyedArchiver.m b/Source/NSKeyedArchiver.m
new file mode 100644
index 000000000..772b8c5ea
--- /dev/null
+++ b/Source/NSKeyedArchiver.m
@@ -0,0 +1,741 @@
+/** Implementation for NSKeyedArchiver for GNUStep
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+ Written by: Richard Frith-Macdonald
+ Date: January 2004
+
+ 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
+#include
+#include
+#include
+#include
+
+/*
+ * Setup for inline operation of pointer map tables.
+ */
+#define GSI_MAP_RETAIN_KEY(M, X)
+#define GSI_MAP_RELEASE_KEY(M, X)
+#define GSI_MAP_RETAIN_VAL(M, X)
+#define GSI_MAP_RELEASE_VAL(M, X)
+#define GSI_MAP_HASH(M, X) ((X).uint)
+#define GSI_MAP_EQUAL(M, X,Y) ((X).uint == (Y).uint)
+#define GSI_MAP_NOCLEAN 1
+
+#include
+
+
+#define _IN_NSKEYEDARCHIVER_M 1
+#include
+#undef _IN_NSKEYEDARCHIVER_M
+
+/* Exceptions */
+NSString * const NSInvalidArchiveOperationException
+= @"NSInvalidArchiveOperationException";
+
+static NSMapTable *globalClassMap = 0;
+
+#define CHECKKEY \
+ if ([aKey isKindOfClass: [NSString class]] == NO) \
+ { \
+ [NSException raise: NSInvalidArgumentException \
+ format: @"%@, bad key '%@' in %@", \
+ NSStringFromClass([self class]), aKey, NSStringFromSelector(_cmd)]; \
+ } \
+ if ([aKey hasPrefix: @"$"] == YES) \
+ { \
+ aKey = [@"$" stringByAppendingString: aKey]; \
+ } \
+ if ([_enc objectForKey: aKey] != nil) \
+ { \
+ [NSException raise: NSInvalidArgumentException \
+ format: @"%@, duplicate key '%@' in %@", \
+ NSStringFromClass([self class]), aKey, NSStringFromSelector(_cmd)]; \
+ }
+
+@interface NSKeyedArchiver (Private)
+- (NSDictionary*) _buildObjectReference: (id)anObject;
+- (void) _encodeObject: (id)anObject
+ forKey: (NSString*)aKey
+ conditional: (BOOL)conditional;
+@end
+
+@implementation NSKeyedArchiver (Private)
+/*
+ * Add an object to the table off all encoded objects, and return a reference.
+ */
+- (NSDictionary*) _buildObjectReference: (id)anObject
+{
+ unsigned ref = 0;
+
+ if (anObject != nil)
+ {
+ ref = [_obj count];
+ [_obj addObject: anObject];
+ }
+ return [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: ref]
+ forKey: @"CF$UID"];
+}
+
+/*
+ * The real workhorse of the archiving process ... this deals with all
+ * archiving of objects.
+ */
+- (void) _encodeObject: (id)anObject
+ forKey: (NSString*)aKey
+ conditional: (BOOL)conditional
+{
+ id original = anObject;
+ GSIMapNode node;
+ id objectInfo = nil; // Encoded object
+ NSMutableDictionary *m = nil;
+ NSNumber *refNum;
+ NSDictionary *keyDict;
+ unsigned ref = 0;
+
+ if (anObject != nil)
+ {
+ /*
+ * Obtain replacement object for the value being encoded.
+ * Notify delegate of progress and set up new mapping if necessary.
+ */
+ node = GSIMapNodeForKey(_repMap, (GSIMapKey)anObject);
+ if (node == 0)
+ {
+ anObject = [original replacementObjectForKeyedArchiver: self];
+ if (_delegate != nil)
+ {
+ if (anObject != nil)
+ {
+ anObject = [_delegate archiver: self
+ willEncodeObject: anObject];
+ }
+ if (original != anObject)
+ {
+ [_delegate archiver: self
+ willReplaceObject: original
+ withObject: anObject];
+ }
+ }
+ GSIMapAddPair(_repMap, (GSIMapKey)original, (GSIMapVal)anObject);
+ }
+ }
+
+ if (anObject != nil)
+ {
+ node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject);
+ if (node == 0)
+ {
+ if (conditional == YES)
+ {
+ node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject);
+ if (node == 0)
+ {
+ ref = [_obj count];
+ GSIMapAddPair(_cIdMap, (GSIMapKey)anObject, (GSIMapVal)ref);
+ /*
+ * Use the null object as a placeholder for a conditionally
+ * encoded object.
+ */
+ [_obj addObject: [_obj objectAtIndex: 0]];
+ }
+ else
+ {
+ /*
+ * This object has already been conditionally encoded.
+ */
+ ref = node->value.uint;
+ }
+ }
+ else
+ {
+// FIXME ... exactly what classes are stored directly???
+ if ([anObject isKindOfClass: [NSString class]] == YES)
+ {
+ // We will store the string object directly.
+ objectInfo = anObject;
+ }
+ else
+ {
+ // We store a dictionary describing the object.
+ m = [NSMutableDictionary new];
+ objectInfo = m;
+ }
+
+ node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject);
+ if (node == 0)
+ {
+ /*
+ * Not encoded ... create dictionary for it.
+ */
+ ref = [_obj count];
+ GSIMapAddPair(_uIdMap, (GSIMapKey)anObject, (GSIMapVal)ref);
+ [_obj addObject: objectInfo];
+ }
+ else
+ {
+ /*
+ * Conditionally encoded ... replace with actual value.
+ */
+ ref = node->value.uint;
+ GSIMapAddPair(_uIdMap, (GSIMapKey)anObject, (GSIMapVal)ref);
+ GSIMapRemoveKey(_cIdMap, (GSIMapKey)anObject);
+ [_obj replaceObjectAtIndex: ref withObject: objectInfo];
+ }
+ RELEASE(m);
+ }
+ }
+ else
+ {
+ ref = node->value.uint;
+ }
+ }
+
+ /*
+ * Store the mapping from aKey to the appropriate entry in _obj
+ */
+ refNum = [[NSNumber alloc] initWithInt: ref];
+ keyDict = [NSDictionary dictionaryWithObject: refNum forKey: @"CF$UID"];
+ [_enc setObject: keyDict forKey: aKey];
+ RELEASE(refNum);
+
+ /*
+ * objectInfo is a dictionary describing the object.
+ */
+ if (objectInfo != nil && m == objectInfo)
+ {
+ NSMutableDictionary *savedEnc = _enc;
+ unsigned savedKeyNum = _keyNum;
+ Class c = [anObject class];
+ NSString *classname;
+ Class mapped;
+
+ /*
+ * Map the class of the object to the actual class it is encoded as.
+ * First ask the object, then apply any name mappings to that value.
+ */
+ mapped = [anObject classForKeyedArchiver];
+ if (mapped != nil)
+ {
+ c = mapped;
+ }
+
+ classname = [self classNameForClass: c];
+ if (classname == nil)
+ {
+ classname = [[self class] classNameForClass: c];
+ }
+ if (classname == nil)
+ {
+ classname = NSStringFromClass(c);
+ }
+ else
+ {
+ c = NSClassFromString(classname);
+ }
+
+ /*
+ * At last, get the object to encode itsself. Save and restore the
+ * current object scope of course.
+ */
+ _enc = m;
+ _keyNum = 0;
+ [anObject encodeWithCoder: self];
+ _keyNum = savedKeyNum;
+ _enc = savedEnc;
+
+ /*
+ * This is ugly, but it seems to be the way MacOS-X does it ...
+ * We create class information by storing it directly into the
+ * table of all objects, and making a reference so we can look
+ * up the table entry by class pointer.
+ * A much cleaner way to do it would be by encoding the class
+ * normally, but we are trying to be compatible.
+ *
+ * Also ... we encode the class *after* encoding the instance,
+ * simply because that seems to be the way MacOS-X does it and
+ * we want to maximise compatibility (perhaps they had good reason?)
+ */
+ node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)c);
+ if (node == 0)
+ {
+ NSMutableDictionary *cDict;
+ NSMutableArray *hierarchy;
+
+ ref = [_obj count];
+ GSIMapAddPair(_uIdMap, (GSIMapKey)c, (GSIMapVal)ref);
+ cDict = [[NSMutableDictionary alloc] initWithCapacity: 2];
+
+ /*
+ * record class name
+ */
+ [cDict setObject: classname forKey: @"$classname"];
+
+ /*
+ * Record the class hierarchy for this object.
+ */
+ hierarchy = [NSMutableArray new];
+ while (c != 0)
+ {
+ Class next = [c superClass];
+
+ [hierarchy addObject: NSStringFromClass(c)];
+ if (next == c)
+ {
+ break;
+ }
+ c = next;
+ }
+ [cDict setObject: hierarchy forKey: @"$classes"];
+ RELEASE(hierarchy);
+ [_obj addObject: cDict];
+ RELEASE(cDict);
+ }
+ else
+ {
+ ref = node->value.uint;
+ }
+
+ /*
+ * Now create a reference to the class information and store it
+ * in the object description dictionary for the object we just encoded.
+ */
+ refNum = [[NSNumber alloc] initWithInt: ref];
+ keyDict = [NSDictionary dictionaryWithObject: refNum forKey: @"CF$UID"];
+ [m setObject: keyDict forKey: @"$class"];
+ RELEASE(refNum);
+ }
+
+ /*
+ * If we have encoded the object information, tell the delegaate.
+ */
+ if (objectInfo != nil && _delegate != nil)
+ {
+ [_delegate archiver: self didEncodeObject: anObject];
+ }
+}
+@end
+
+@implementation NSKeyedArchiver
+
+/*
+ * When I tried this on MacOS 10.3 it encoded the object with the key 'root',
+ * so this implementation does the same.
+ */
++ (NSData*) archivedDataWithRootObject: (id)anObject
+{
+ NSMutableData *m = nil;
+ NSKeyedArchiver *a = nil;
+ NSData *d = nil;
+
+ NS_DURING
+ {
+ m = [[NSMutableData alloc] initWithCapacity: 10240];
+ a = [[NSKeyedArchiver alloc] initForWritingWithMutableData: m];
+ [a encodeObject: anObject forKey: @"root"];
+ [a finishEncoding];
+ d = [m copy];
+ DESTROY(m);
+ DESTROY(a);
+ }
+ NS_HANDLER
+ {
+ DESTROY(m);
+ DESTROY(a);
+ [localException raise];
+ }
+ NS_ENDHANDLER
+ return AUTORELEASE(d);
+}
+
++ (BOOL) archiveRootObject: (id)anObject toFile: (NSString*)aPath
+{
+ CREATE_AUTORELEASE_POOL(pool);
+ NSData *d;
+ BOOL result;
+
+ d = [self archivedDataWithRootObject: anObject];
+ result = [d writeToFile: aPath atomically: YES];
+ RELEASE(pool);
+ return result;
+}
+
++ (NSString*) classNameForClass: (Class)aClass
+{
+ return (NSString*)NSMapGet(globalClassMap, (void*)aClass);
+}
+
++ (void) initialize
+{
+ if (globalClassMap == 0)
+ {
+ globalClassMap =
+ NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
+ NSObjectMapValueCallBacks, 0);
+ }
+}
+
++ (void) setClassName: (NSString*)aString forClass: (Class)aClass
+{
+ if (aString == nil)
+ {
+ NSMapRemove(globalClassMap, (void*)aClass);
+ }
+ else
+ {
+ NSMapInsert(globalClassMap, (void*)aClass, aString);
+ }
+}
+
+- (NSString*) classNameForClass: (Class)aClass
+{
+ return (NSString*)NSMapGet(_clsMap, (void*)aClass);
+}
+
+- (void) dealloc
+{
+ RELEASE(_enc);
+ RELEASE(_obj);
+ RELEASE(_data);
+ if (_clsMap != 0)
+ {
+ NSFreeMapTable(_clsMap);
+ _clsMap = 0;
+ }
+ if (_cIdMap)
+ {
+ GSIMapEmptyMap(_cIdMap);
+ if (_uIdMap)
+ {
+ GSIMapEmptyMap(_uIdMap);
+ }
+ if (_repMap)
+ {
+ GSIMapEmptyMap(_repMap);
+ }
+ NSZoneFree(_cIdMap->zone, (void*)_cIdMap);
+ }
+ [super dealloc];
+}
+
+- (id) delegate
+{
+ return _delegate;
+}
+
+- (void) encodeBool: (BOOL)aBool forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSNumber numberWithBool: aBool] forKey: aKey];
+}
+
+- (void) encodeBytes: (const uint8_t*)aPointer length: (unsigned)length forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSData dataWithBytes: aPointer length: length]
+ forKey: aKey];
+}
+
+- (void) encodeConditionalObject: (id)anObject
+{
+ [self _encodeObject: anObject
+ forKey: [NSString stringWithFormat: @"$%u", _keyNum++]
+ conditional: YES];
+}
+
+- (void) encodeConditionalObject: (id)anObject forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [self _encodeObject: anObject forKey: aKey conditional: YES];
+}
+
+- (void) encodeDouble: (double)aDouble forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSNumber numberWithDouble: aDouble] forKey: aKey];
+}
+
+- (void) encodeFloat: (float)aFloat forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSNumber numberWithFloat: aFloat] forKey: aKey];
+}
+
+- (void) encodeInt: (int)anInteger forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSNumber numberWithInt: anInteger] forKey: aKey];
+}
+
+- (void) encodeInt32: (int32_t)anInteger forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSNumber numberWithLong: anInteger] forKey: aKey];
+}
+
+- (void) encodeInt64: (int64_t)anInteger forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [_enc setObject: [NSNumber numberWithLongLong: anInteger] forKey: aKey];
+}
+
+- (void) encodeObject: (id)anObject
+{
+ [self _encodeObject: anObject
+ forKey: [NSString stringWithFormat: @"$%u", _keyNum++]
+ conditional: NO];
+}
+
+- (void) encodeObject: (id)anObject forKey: (NSString*)aKey
+{
+ CHECKKEY
+
+ [self _encodeObject: anObject forKey: aKey conditional: NO];
+}
+
+- (void) encodeValueOfObjCType: (const char*)type
+ at: (const void*)address
+{
+ NSString *aKey;
+ id o;
+
+ if (*type == _C_ID || *type == _C_CLASS)
+ {
+ [self encodeObject: *(id*)address];
+ return;
+ }
+
+ aKey = [NSString stringWithFormat: @"$%u", _keyNum++];
+ switch (*type)
+ {
+ case _C_SEL:
+ {
+ // Selectors are encoded by name as strings.
+ o = NSStringFromSelector(*(SEL*)address);
+ [self encodeObject: o];
+ }
+ return;
+
+ case _C_CHARPTR:
+ {
+ /*
+ * Bizzarely MacOS-X seems to encode char* values by creating
+ * string objects and encoding those objects!
+ */
+ o = [NSString stringWithCString: (char*)address];
+ [self encodeObject: o];
+ }
+ return;
+
+ case _C_CHR:
+ o = [NSNumber numberWithInt: (int)*(char*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_UCHR:
+ o = [NSNumber numberWithInt: (int)*(unsigned char*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_SHT:
+ o = [NSNumber numberWithInt: (int)*(short*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_USHT:
+ o = [NSNumber numberWithLong: (long)*(unsigned short*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_INT:
+ o = [NSNumber numberWithInt: *(int*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_UINT:
+ o = [NSNumber numberWithUnsignedInt: *(unsigned int*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_LNG:
+ o = [NSNumber numberWithLong: *(long*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_ULNG:
+ o = [NSNumber numberWithUnsignedLong: *(unsigned long*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_LNG_LNG:
+ o = [NSNumber numberWithLongLong: *(long long*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_ULNG_LNG:
+ o = [NSNumber numberWithUnsignedLongLong:
+ *(unsigned long long*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_FLT:
+ o = [NSNumber numberWithFloat: *(float*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_DBL:
+ o = [NSNumber numberWithDouble: *(double*)address];
+ [_enc setObject: o forKey: aKey];
+ return;
+
+ case _C_STRUCT_B:
+ [NSException raise: NSInvalidArgumentException
+ format: @"-[%@ %@]: this archiver cannote encode structs",
+ NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+ return;
+
+ default: /* Types that can be ignored in first pass. */
+ [NSException raise: NSInvalidArgumentException
+ format: @"-[%@ %@]: unknown type encoding ('%c')",
+ NSStringFromClass([self class]), NSStringFromSelector(_cmd), *type];
+ break;
+ }
+}
+
+- (void) finishEncoding
+{
+ NSMutableDictionary *final;
+ NSData *data;
+ NSString *error;
+
+ [_delegate archiverWillFinish: self];
+
+ final = [NSMutableDictionary new];
+ [final setObject: NSStringFromClass([self class]) forKey: @"$archiver"];
+ [final setObject: @"100000" forKey: @"$version"];
+ [final setObject: _enc forKey: @"$top"];
+ [final setObject: _obj forKey: @"$objects"];
+ data = [NSPropertyListSerialization dataFromPropertyList: final
+ format: _format
+ errorDescription: &error];
+ RELEASE(final);
+ [_data setData: data];
+ [_delegate archiverDidFinish: self];
+}
+
+- (id) initForWritingWithMutableData: (NSMutableData*)data
+{
+ self = [super init];
+ if (self)
+ {
+ NSZone *zone = [self zone];
+
+ _keyNum = 0;
+ _data = RETAIN(data);
+
+ _clsMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
+ NSObjectMapValueCallBacks, 0);
+ /*
+ * Set up map tables.
+ */
+ _cIdMap = (GSIMapTable)NSZoneMalloc(zone, sizeof(GSIMapTable_t)*5);
+ _uIdMap = &_cIdMap[1];
+ _repMap = &_cIdMap[2];
+ GSIMapInitWithZoneAndCapacity(_cIdMap, zone, 10);
+ GSIMapInitWithZoneAndCapacity(_uIdMap, zone, 200);
+ GSIMapInitWithZoneAndCapacity(_repMap, zone, 1);
+
+ _enc = [NSMutableDictionary new]; // Top level mapping dict
+ _obj = [NSMutableArray new]; // Array of objects.
+ [_obj addObject: @"$null"]; // Placeholder.
+
+ }
+ return self;
+}
+
+- (NSPropertyListFormat) outputFormat
+{
+ return _format;
+}
+
+- (void) setClassName: (NSString*)aString forClass: (Class)aClass
+{
+ if (aString == nil)
+ {
+ NSMapRemove(_clsMap, (void*)aClass);
+ }
+ else
+ {
+ NSMapInsert(_clsMap, (void*)aClass, aString);
+ }
+}
+
+- (void) setDelegate: (id)anObject
+{
+ _delegate = anObject; // Not retained.
+}
+
+- (void) setOutputFormat: (NSPropertyListFormat)format
+{
+ _format = format;
+}
+
+@end
+
+@implementation NSObject (NSKeyedArchiverDelegate)
+- (void) archiver: (NSKeyedArchiver*)anArchiver didEncodeObject: (id)anObject
+{
+}
+- (id) archiver: (NSKeyedArchiver*)anArchiver willEncodeObject: (id)anObject
+{
+ return anObject;
+}
+- (void) archiverDidFinish: (NSKeyedArchiver*)anArchiver
+{
+}
+- (void) archiverWillFinish: (NSKeyedArchiver*)anArchiver
+{
+}
+- (void) archiver: (NSKeyedArchiver*)anArchiver
+willReplaceObject: (id)anObject
+ withObject: (id)newObject
+{
+}
+@end
+
+@implementation NSObject (NSKeyedArchiverObjectSubstitution)
+- (Class) classForKeyedArchiver
+{
+ return [self classForArchiver];
+}
+- (id) replacementObjectForKeyedArchiver: (NSKeyedArchiver*)archiver
+{
+ return [self replacementObjectForArchiver: nil];
+}
+@end
+
diff --git a/Source/NSKeyedUnarchiver.m b/Source/NSKeyedUnarchiver.m
new file mode 100644
index 000000000..872f1ba98
--- /dev/null
+++ b/Source/NSKeyedUnarchiver.m
@@ -0,0 +1,230 @@
+/** Implementation for NSKeyedUnarchiver for GNUStep
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+ Written by: Richard Frith-Macdonald
+ Date: January 2004
+
+ 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
+#include
+#include
+#include
+
+#include
+#include
+
+#define _IN_NSKEYEDUNARCHIVER_M 1
+#include
+#undef _IN_NSKEYEDUNARCHIVER_M
+
+static NSMapTable globalClassMap = 0;
+
+NSString * const NSInvalidUnarchiveOperationException
+ = @"NSInvalidUnarchiveOperationException";
+
+@implementation NSKeyedUnarchiver
+
++ (Class) classForClassName: (NSString*)aString
+{
+ return (Class)NSMapGet(globalClassMap, (void*)aString);
+}
+
++ (void) initialize
+{
+ if (globalClassMap == 0)
+ {
+ globalClassMap =
+ NSCreateMapTable(NSObjectMapKeyCallBacks,
+ NSNonOwnedPointerMapValueCallBacks, 0);
+ }
+}
+
++ (void) setClass: (Class)aClass forClassName: (NSString*)aString
+{
+ if (aClass == nil)
+ {
+ NSMapRemove(globalClassMap, (void*)aString);
+ }
+ else
+ {
+ NSMapInsert(globalClassMap, (void*)aString, aClass);
+ }
+}
+
+/*
+ * When I tried this on MacOS 10.3 it encoded the object with the key 'root',
+ * so this implementation does the same.
+ */
++ (id) unarchiveObjectWithData: (NSData*)data
+{
+ NSKeyedUnarchiver *u = nil;
+ id o = nil;
+
+ NS_DURING
+ {
+ u = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
+ o = RETAIN([u decodeObjectForKey: @"root"]);
+ [u finishDecoding];
+ DESTROY(u);
+ }
+ NS_HANDLER
+ {
+ DESTROY(u);
+ [localException raise];
+ }
+ NS_ENDHANDLER
+ return AUTORELEASE(o);
+}
+
++ (id) unarchiveObjectWithFile: (NSString*)aPath
+{
+ CREATE_AUTORELEASE_POOL(pool);
+ NSData *d;
+ id o;
+
+ d = [NSData dataWithContentsOfFile: aPath];
+ o = [self unarchiveObjectWithData: d];
+ RETAIN(o);
+ RELEASE(pool);
+ return AUTORELEASE(o);
+}
+
+- (Class) classForClassName: (NSString*)aString
+{
+ [self notImplemented: _cmd];
+ return nil;
+}
+
+- (BOOL) containsValueForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return NO;
+}
+
+- (BOOL) decodeBoolForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return NO;
+}
+
+- (const uint8_t*) decodeBytesForKey: (NSString*)aKey
+ returnedLength: (unsigned*)length
+{
+ [self notImplemented: _cmd];
+ *length = 0;
+ return 0;
+}
+
+- (double) decodeDoubleForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return 0.0;
+}
+
+- (float) decodeFloatForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return 0.0;
+}
+
+- (int) decodeIntForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return 0;
+}
+
+- (int32_t) decodeInt32ForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return 0;
+}
+
+- (int64_t) decodeInt64ForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return 0;
+}
+
+- (id) decodeObjectForKey: (NSString*)aKey
+{
+ [self notImplemented: _cmd];
+ return nil;
+}
+
+- (id) delegate
+{
+ return _delegate;
+}
+
+- (void) finishDecoding
+{
+ [self notImplemented: _cmd];
+}
+
+- (id) initForReadingWithData: (NSData*)data
+{
+ [self notImplemented: _cmd];
+ return self;
+}
+
+- (void) setClass: (Class)aClass forClassName: (NSString*)aString
+{
+ [self notImplemented: _cmd];
+}
+
+- (void) setDelegate: (id)delegate
+{
+ _delegate = delegate; // Not retained.
+}
+
+@end
+
+@implementation NSObject (NSKeyedUnarchiverDelegate)
+- (Class) unarchiver: (NSKeyedUnarchiver*)anUnarchiver
+ cannotDecodeObjectOfClassName: (NSString*)aName
+ originalClasses: (NSArray*)classNames
+{
+ return nil;
+}
+- (id) unarchiver: (NSKeyedUnarchiver*)anUnarchiver
+ didDecodeObject: (id)anObject
+{
+ return anObject;
+}
+- (void) unarchiverDidFinish: (NSKeyedUnarchiver*)anUnarchiver
+{
+}
+- (void) unarchiverWillFinish: (NSKeyedUnarchiver*)anUnarchiver
+{
+}
+- (void) unarchiver: (NSKeyedUnarchiver*)anUnarchiver
+ willReplaceObject: (id)anObject
+ withObject: (id)newObject
+{
+}
+@end
+
+@implementation NSObject (NSKeyedUnarchiverObjectSubstitution)
++ (Class) classForKeyedUnarchiver
+{
+ return self;
+}
+@end
+
diff --git a/Source/NSPropertyList.m b/Source/NSPropertyList.m
new file mode 100644
index 000000000..87c1958a4
--- /dev/null
+++ b/Source/NSPropertyList.m
@@ -0,0 +1,2178 @@
+/** Interface for NSPropertyList for GNUStep
+ Copyright (C) 2003,2004 Free Software Foundation, Inc.
+
+ Written by: Richard Frith-Macdonald
+
+ 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
+#include "GNUstepBase/preface.h"
+#include "GNUstepBase/GSMime.h"
+
+#include "Foundation/NSArray.h"
+#include "Foundation/NSAutoreleasePool.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/NSUserDefaults.h"
+#include "Foundation/NSValue.h"
+#include "Foundation/NSDebug.h"
+
+extern BOOL GSScanDouble(unichar*, unsigned, double*);
+
+@class GSString;
+@class GSMutableString;
+@class GSMutableArray;
+@class GSMutableDictionary;
+
+
+/*
+ * 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];
+ }
+}
+
+static id GSPropertyListFromStringsFormat(NSString *string);
+
+
+#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 = pld->pos - start - shrink;
+ unichar *chars;
+ unsigned j;
+ unsigned k;
+
+ chars = NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * length);
+ escaped = 0;
+ hex = NO;
+ for (j = start, k = 0; j < pld->pos; j++)
+ {
+ unsigned char c = pld->ptr[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++;
+ }
+ }
+ }
+
+ 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 in string";
+ 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 in string";
+ 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 = @"";
+ }
+ result = [NSCalendarDate dateWithString: content
+ calendarFormat: @"%Y-%m-%d %H:%M:%S %z"];
+ }
+ else if ([name isEqualToString: @"data"])
+ {
+ result = [GSMimeDocument decodeBase64String: content];
+ 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
+
+static id
+GSPropertyListFromStringsFormat(NSString *string)
+{
+ NSMutableDictionary *dict;
+ pldata _pld;
+ pldata *pld = &_pld;
+ unsigned length = [string length];
+ NSData *d;
+
+ /*
+ * An empty string is a nil property list.
+ */
+ if (length == 0)
+ {
+ return nil;
+ }
+
+ d = [string dataUsingEncoding: NSUnicodeStringEncoding];
+ _pld.ptr = (unsigned char*)[d bytes];
+ _pld.pos = 1;
+ _pld.end = length + 1;
+ _pld.err = nil;
+ _pld.lin = 1;
+ _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, _pld.pos, _pld.err];
+ }
+ return AUTORELEASE(dict);
+}
+
+
+
+#include
+
+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 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 '\a':
+ case '\b':
+ case '\t':
+ case '\r':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\\':
+ case '\'' :
+ case '"' :
+ len += 2;
+ break;
+
+ default:
+ if (*from < 128)
+ {
+ if (isprint(*from) || *from == ' ')
+ {
+ len++;
+ }
+ else
+ {
+ len += 4;
+ }
+ }
+ else
+ {
+ len += 6;
+ }
+ break;
+ }
+ }
+
+ [output setLength: [output length] + len + 2];
+ ptr = [output mutableBytes] + [output length];
+ *ptr++ = '"';
+ for (from = ustring; from < end; from++)
+ {
+ switch (*from)
+ {
+ case '\a': *ptr++ = '\\'; *ptr++ = 'a'; break;
+ case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break;
+ case '\t': *ptr++ = '\\'; *ptr++ = 't'; break;
+ case '\r': *ptr++ = '\\'; *ptr++ = 'r'; break;
+ case '\n': *ptr++ = '\\'; *ptr++ = 'n'; break;
+ case '\v': *ptr++ = '\\'; *ptr++ = 'v'; break;
+ case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break;
+ case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break;
+ case '\'': *ptr++ = '\\'; *ptr++ = '\''; break;
+ case '"' : *ptr++ = '\\'; *ptr++ = '"'; break;
+
+ default:
+ if (*from < 128)
+ {
+ if (isprint(*from) || *from == ' ')
+ {
+ *ptr++ = *from;
+ }
+ else
+ {
+ unichar c = *from;
+
+ sprintf(ptr, "\\%03o", c);
+ ptr = &ptr[4];
+ }
+ }
+ else
+ {
+ sprintf(ptr, "\\u%04x", *from);
+ ptr = &ptr[6];
+ }
+ 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"
+};
+
+#define PLNEW 0 // New extended OpenStep property list
+#define PLXML 1 // New MacOS-X XML property list
+#define PLOLD 2 // Old (standard) OpenStep property list
+#define PLDSC 3 // Just a description
+/**
+ * obj is the object to be written out
+ * loc is the locale for formatting (or nil to indicate no formatting)
+ * lev is the level of indentation to use
+ * step is the indentation step (0 == 0, 1 = 2, 2 = 4, 3 = 8)
+ * x is an indicator for xml or old/new openstep property list format
+ * 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: "" length: 8];
+ XString(obj, dest);
+ [dest appendBytes: "\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: "\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: "\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: "" length: 8];
+ XString([obj stringValue], dest);
+ [dest appendBytes: "\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: "" length: 6];
+ XString([obj stringValue], dest);
+ [dest appendBytes: "" length: 7];
+ }
+ 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: "\n" length: 7];
+ encodeBase64(obj, dest);
+ [dest appendBytes: "\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+3];
+ 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&0x3) == 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]])
+ {
+ if (x == NSPropertyListXMLFormat_v1_0)
+ {
+ [dest appendBytes: "" length: 6];
+ obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"
+ timeZone: nil locale: nil];
+ obj = [obj dataUsingEncoding: NSASCIIStringEncoding];
+ [dest appendData: obj];
+ [dest appendBytes: "\n" length: 8];
+ }
+ else if (x == NSPropertyListGNUStepFormat)
+ {
+ [dest appendBytes: "<*D" length: 3];
+ obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"
+ timeZone: nil 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: nil 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: "\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: "\n" length: 9];
+ }
+ else
+ {
+ unsigned count = [obj count];
+ unsigned last = count - 1;
+ NSString *plists[count];
+ unsigned i;
+
+ [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;
+ 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;
+ id key;
+
+ [dest appendBytes: "\n" length: 7];
+ e = [obj keyEnumerator];
+ while ((key = [e nextObject]))
+ {
+ id val;
+
+ val = [obj objectForKey: key];
+ [dest appendBytes: iSizeString length: strlen(iSizeString)];
+ [dest appendBytes: "" length: 5];
+ XString(key, dest);
+ [dest appendBytes: "\n" length: 7];
+ [dest appendBytes: iSizeString length: strlen(iSizeString)];
+ OAppend(val, loc, level, step, x, dest);
+ }
+ [dest appendBytes: iBaseString length: strlen(iBaseString)];
+ [dest appendBytes: "\n" length: 8];
+ }
+ else
+ {
+ 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;
+
+ [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
+ }
+
+ for (i = 0; i < numKeys; i++)
+ {
+ plists[i] = (*myObj)(obj, objSel, keys[i]);
+ }
+
+ 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
+ {
+ if (x == NSPropertyListXMLFormat_v1_0)
+ {
+ NSDebugLog(@"Non-property-list class (%@) encoded as string",
+ NSStringFromClass([obj class]));
+ [dest appendBytes: "" length: 8];
+ XString([obj description], dest);
+ [dest appendBytes: "" length: 9];
+ }
+ else
+ {
+ NSDebugLog(@"Non-property-list class (%@) encoded as string",
+ NSStringFromClass([obj class]));
+ PString([obj description], dest);
+ }
+ }
+}
+
+
+
+
+@implementation NSPropertyListSerialization
+
++ (void) initialize
+{
+ static BOOL beenHere = NO;
+
+ if (beenHere == NO)
+ {
+ beenHere = 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 =
+ "\n\n"
+ "\n";
+
+ [dest appendBytes: prefix length: strlen(prefix)];
+ OAppend(aPropertyList, loc, 0, step > 3 ? 3 : step, aFormat, dest);
+ [dest appendBytes: "" length: 8];
+ }
+ else if (aFormat == NSPropertyListGNUstepBinaryFormat)
+ {
+ [NSSerializer serializePropertyList: aPropertyList intoData: dest];
+ }
+ else
+ {
+ OAppend(aPropertyList, loc, 0, step > 3 ? 3 : step, aFormat, dest);
+ }
+ return dest;
+}
+
++ (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:
+ 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;
+ 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 (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] == '?')
+ {
+#ifdef HAVE_LIBXML
+ // It begins with '' so it is xml
+ format = NSPropertyListXMLFormat_v1_0;
+#else
+ error = @"XML format not supported ... XML support notn present.";
+#endif
+ }
+ else
+ {
+ // Assume openstep format unless we find otherwise.
+ format = NSPropertyListOpenStepFormat;
+ }
+ }
+ }
+
+ if (error == nil)
+ {
+ switch (format)
+ {
+ 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;
+
+ case NSPropertyListOpenStepFormat:
+ {
+ pldata _pld;
+
+ _pld.ptr = bytes;
+ _pld.pos = 1;
+ _pld.end = length + 1;
+ _pld.err = nil;
+ _pld.lin = 1;
+ _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 = NSPropertyListGNUstepBinaryFormat;
+ }
+ if (_pld.err != nil)
+ {
+ error = [NSString stringWithFormat:
+ @"Parse failed at line %d (char %d) - %@",
+ _pld.lin, _pld.pos, _pld.err];
+ }
+ }
+ break;
+
+ case NSPropertyListGNUstepBinaryFormat:
+ if (anOption == NSPropertyListImmutable)
+ {
+ result = [NSDeserializer deserializePropertyListFromData: data
+ mutableContainers: NO];
+ }
+ else
+ {
+ result = [NSDeserializer deserializePropertyListFromData: data
+ mutableContainers: YES];
+ }
+ break;
+
+ default:
+ error = @"format not supported";
+ break;
+ }
+ }
+
+ /*
+ * Done ... return all values.
+ */
+ if (anErrorString != 0)
+ {
+ *anErrorString = error;
+ }
+ if (aFormat != 0)
+ {
+ *aFormat = format;
+ }
+ return result;
+}
+
+@end