ADDED ChangeLog Index: ChangeLog ================================================================== --- ChangeLog +++ ChangeLog @@ -0,0 +1,23 @@ +ObjFW 0.1.1 -> 0.1.2, 15.01.2010 + * Fix a bug in OFMutableArray's -[removeObject:] and + -[removeObjectIdenticalTo:] that could lead to not removing all + occurrences of the object from the array and to out of bounds reads. + * Change the URL in the framework plist to the homepage. + +ObjFW 0.1 -> 0.1.1, 04.01.2010 + * Fix a missing out of range check for -[removeNItems:atIndex:] that + allowed the programmer to specify too big ranges so it would crash + instead of throwing an exception. + * Fix missing calls to -[retain] and -[autorelease] when getting + objects from an OFArray or OFDictionary. + * Safer and more fault-tolerant way to remove objects from an + OFMutableArray. + * Calling +[dealloc] throws an exception now. If someone really calls + [SomeClass dealloc], this should be punished and not ignored, as + this is a serious programmer error. + * -[readLineWithEncoding:] is more fault-tolerant now and does not + lose data when it stumbles upon invalid encoding. Instead, it allows + recalling with the correct encoding now. + +ObjFW 0.1, 24.12.2009 + + Initial release Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -243,11 +243,11 @@ ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "rm -fr $TARGET_BUILD_DIR/ObjFW.framework\nmkdir -p $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Headers || exit 1\nmkdir -p $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Resources || exit 1\ninstall -m 755 src/ObjFW $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/ || exit 1\ninstall -m 644 src/*.h $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Headers/ || exit 1\ncat >$TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Resources/Info.plist <<__EOF__\n\n\n\n\n\tCFBundleDevelopmentRegion\n\tEnglish\n\tCFBundleExecutable\n\tObjFW\n\tCFBundleGetInfoString\n\thttps://webkeks.org/hg/objfw\n\tCFBundleIdentifier\n\tObjFW\n\tCFBundleInfoDictionaryVersion\n\t6.0\n\tCFBundleName\n\tobjfw\n\tCFBundlePackageType\n\tFMWK\n\tCFBundleShortVersionString\n\t0.1\n\tCFBundleSignature\n\tOBJF\n\tCFBundleVersion\n\t0.1\n\n\n__EOF__\nif [ ! -f \"$TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Resources/Info.plist\" ]; then\n\texit 1;\nfi\nln -s A $TARGET_BUILD_DIR/ObjFW.framework/Versions/Current || exit 1\nln -s Versions/Current/Headers $TARGET_BUILD_DIR/ObjFW.framework/Headers || exit 1\nln -s Versions/Current/Resources $TARGET_BUILD_DIR/ObjFW.framework/Resources || exit 1\nln -s Versions/Current/ObjFW $TARGET_BUILD_DIR/ObjFW.framework/ObjFW || exit 1"; + shellScript = "rm -fr $TARGET_BUILD_DIR/ObjFW.framework\nmkdir -p $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Headers || exit 1\nmkdir -p $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Resources || exit 1\ninstall -m 755 src/ObjFW $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/ || exit 1\ninstall -m 644 src/*.h $TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Headers/ || exit 1\ncat >$TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Resources/Info.plist <<__EOF__\n\n\n\n\n\tCFBundleDevelopmentRegion\n\tEnglish\n\tCFBundleExecutable\n\tObjFW\n\tCFBundleGetInfoString\n\thttps://webkeks.org/objfw\n\tCFBundleIdentifier\n\tObjFW\n\tCFBundleInfoDictionaryVersion\n\t6.0\n\tCFBundleName\n\tobjfw\n\tCFBundlePackageType\n\tFMWK\n\tCFBundleShortVersionString\n\t0.1\n\tCFBundleSignature\n\tOBJF\n\tCFBundleVersion\n\t0.1\n\n\n__EOF__\nif [ ! -f \"$TARGET_BUILD_DIR/ObjFW.framework/Versions/A/Resources/Info.plist\" ]; then\n\texit 1;\nfi\nln -s A $TARGET_BUILD_DIR/ObjFW.framework/Versions/Current || exit 1\nln -s Versions/Current/Headers $TARGET_BUILD_DIR/ObjFW.framework/Headers || exit 1\nln -s Versions/Current/Resources $TARGET_BUILD_DIR/ObjFW.framework/Resources || exit 1\nln -s Versions/Current/ObjFW $TARGET_BUILD_DIR/ObjFW.framework/ObjFW || exit 1"; }; 4B5D8DE11099E1BF000896FF /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( DELETED TODO Index: TODO ================================================================== --- TODO +++ TODO @@ -1,25 +0,0 @@ -Test if autorelease pool releases everything correctly when thread is ended - -Make strings \0-safe - -Cleanup OFXMLElement - -Add finalization to OFXMLParser -Add namespaces to OFXMLParser -Add support for different encodings to OFXMLParser - -OFTime - -Tests for OFFile. -Tests for OFNumber. -Tests for readLine:. - -Serialization - -OFQueue -OFSortedArray - -OFBase64 -OFDirectory - -... and of course enhance the already existing classes! Index: src/OFArray.m ================================================================== --- src/OFArray.m +++ src/OFArray.m @@ -167,11 +167,11 @@ return new; } - (id)objectAtIndex: (size_t)index { - return *((OFObject**)[array itemAtIndex: index]); + return [[*((OFObject**)[array itemAtIndex: index]) retain] autorelease]; } - (size_t)indexOfObject: (OFObject*)obj { id *objs = [array cArray]; @@ -204,18 +204,18 @@ - (id)firstObject { id *first = [array firstItem]; - return (first != NULL ? *first : nil); + return (first != NULL ? [[*first retain] autorelease] : nil); } - (id)lastObject { id *last = [array lastItem]; - return (last != NULL ? *last : nil); + return (last != NULL ? [[*last retain] autorelease] : nil); } - (OFString*)componentsJoinedByString: (OFString*)separator { OFString *str; @@ -222,27 +222,22 @@ OFString **objs = [array cArray]; size_t i, count = [array count]; IMP append; if (count == 0) - return [OFString string]; - - str = [[OFMutableString alloc] init]; - @try { - append = [str methodForSelector: @selector(appendString:)]; - - for (i = 0; i < count - 1; i++) { - append(str, @selector(appendString:), objs[i]); - append(str, @selector(appendString:), separator); - } - append(str, @selector(appendString:), objs[i]); - } @catch (OFException *e) { - [str release]; - @throw e; - } - - return [str autorelease]; + return @""; + + str = [OFMutableString string]; + append = [str methodForSelector: @selector(appendString:)]; + + for (i = 0; i < count - 1; i++) { + append(str, @selector(appendString:), objs[i]); + append(str, @selector(appendString:), separator); + } + append(str, @selector(appendString:), objs[i]); + + return str; } - (BOOL)isEqual: (OFObject*)obj { OFObject **objs, **objs2; Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -473,11 +473,11 @@ /* Key not in dictionary */ if (i >= size || ![data[i].key isEqual: key]) return nil; - return data[i].object; + return [[data[i].object retain] autorelease]; } - (size_t)count { return count; Index: src/OFMutableArray.m ================================================================== --- src/OFMutableArray.m +++ src/OFMutableArray.m @@ -8,10 +8,12 @@ * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ #include "config.h" + +#include #import "OFMutableArray.h" #import "OFExceptions.h" @implementation OFMutableArray @@ -105,12 +107,25 @@ OFObject **objs = [array cArray]; size_t i, count = [array count]; for (i = 0; i < count; i++) { if ([objs[i] isEqual: obj]) { - [objs[i] release]; + OFObject *obj = objs[i]; [array removeItemAtIndex: i]; + [obj release]; + + /* + * We need to get the C array again as it might have + * been relocated. We also need to adjust the count + * as otherwise we would have an out of bounds access. + * As another object will be at the current index now, + * we also need to handle the same index again, thus we + * decrease it. + */ + objs = [array cArray]; + count--; + i--; } } return self; } @@ -120,12 +135,24 @@ OFObject **objs = [array cArray]; size_t i, count = [array count]; for (i = 0; i < count; i++) { if (objs[i] == obj) { - [obj release]; [array removeItemAtIndex: i]; + [obj release]; + + /* + * We need to get the C array again as it might have + * been relocated. We also need to adjust the count + * as otherwise we would have an out of bounds access. + * As another object will be at the current index now, + * we also need to handle the same index again, thus we + * decrease it. + */ + objs = [array cArray]; + count--; + i--; } } return self; } @@ -136,37 +163,53 @@ atIndex: index]; } - removeNObjects: (size_t)nobjects { - OFObject **objs = [array cArray]; + OFObject **objs = [array cArray], **copy; size_t i, count = [array count]; if (nobjects > count) @throw [OFOutOfRangeException newWithClass: isa]; - for (i = count - nobjects; i < count; i++) - [objs[i] release]; + copy = [self allocMemoryForNItems: nobjects + withSize: sizeof(OFObject*)]; + memcpy(copy, objs + (count - nobjects), nobjects * sizeof(OFObject*)); + + @try { + [array removeNItems: nobjects]; - [array removeNItems: nobjects]; + for (i = 0; i < nobjects; i++) + [copy[i] release]; + } @finally { + [self freeMemory: copy]; + } return self; } - removeNObjects: (size_t)nobjects atIndex: (size_t)index { - OFObject **objs = [array cArray]; + OFObject **objs = [array cArray], **copy; size_t i, count = [array count]; - if (nobjects > count) + if (nobjects > count - index) @throw [OFOutOfRangeException newWithClass: isa]; - for (i = index; i < count && i < index + nobjects; i++) - [objs[i] release]; + copy = [self allocMemoryForNItems: nobjects + withSize: sizeof(OFObject*)]; + memcpy(copy, objs + index, nobjects * sizeof(OFObject*)); + + @try { + [array removeNItems: nobjects + atIndex: index]; - [array removeNItems: nobjects - atIndex: index]; + for (i = 0; i < nobjects; i++) + [copy[i] release]; + } @finally { + [self freeMemory: copy]; + } return self; } @end Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -554,7 +554,9 @@ { } + (void)dealloc { + @throw [OFNotImplementedException newWithClass: self + selector: _cmd]; } @end Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -11,10 +11,11 @@ #include "config.h" #include #include +#include #import "OFStream.h" #import "OFExceptions.h" #import "OFMacros.h" @@ -135,103 +136,90 @@ } /* Read until we get a newline or \0 */ tmp = [self allocMemoryWithSize: pagesize]; - for (;;) { - if ([self atEndOfStreamWithoutCache]) { - [self freeMemory: tmp]; - - if (cache == NULL) - return nil; - - ret = [OFString stringWithCString: cache - encoding: encoding - length: cache_len]; - - [self freeMemory: cache]; - cache = NULL; - cache_len = 0; - - return ret; - } - - @try { + @try { + for (;;) { + if ([self atEndOfStreamWithoutCache]) { + if (cache == NULL) + return nil; + + ret = [OFString stringWithCString: cache + encoding: encoding + length: cache_len]; + + [self freeMemory: cache]; + cache = NULL; + cache_len = 0; + + return ret; + } + len = [self readNBytesWithoutCache: pagesize intoBuffer: tmp]; - } @catch (OFException *e) { - [self freeMemory: tmp]; - @throw e; - } - - /* Look if there's a newline or \0 */ - for (i = 0; i < len; i++) { - if (OF_UNLIKELY(tmp[i] == '\n' || tmp[i] == '\0')) { - ret_len = cache_len + i; - @try { + + /* Look if there's a newline or \0 */ + for (i = 0; i < len; i++) { + if (OF_UNLIKELY(tmp[i] == '\n' || + tmp[i] == '\0')) { + ret_len = cache_len + i; ret_c = [self allocMemoryWithSize: ret_len]; - } @catch (OFException *e) { - [self freeMemory: tmp]; - @throw e; - } - if (cache != NULL) - memcpy(ret_c, cache, cache_len); - memcpy(ret_c + cache_len, tmp, i); - - if (i < len) { + + if (cache != NULL) + memcpy(ret_c, cache, cache_len); + memcpy(ret_c + cache_len, tmp, i); + @try { + ret = [OFString + stringWithCString: ret_c + encoding: encoding + length: ret_len]; + } @finally { + [self freeMemory: ret_c]; + } + + if (i < len) { tmp2 = [self allocMemoryWithSize: len - i - 1]; - } @catch (OFException *e) { - [self freeMemory: ret_c]; - [self freeMemory: tmp]; - @throw e; - } - memcpy(tmp2, tmp + i + 1, len - i - 1); - - [self freeMemory: cache]; - cache = tmp2; - cache_len = len - i - 1; - } else { - [self freeMemory: cache]; - cache = NULL; - cache_len = 0; - } - [self freeMemory: tmp]; - - @try { - ret = [OFString - stringWithCString: ret_c - encoding: encoding - length: ret_len]; - } @finally { - [self freeMemory: ret_c]; - } - return ret; - } - } - - /* There was no newline or \0 */ - @try { + memcpy(tmp2, tmp + i + 1, + len - i - 1); + + [self freeMemory: cache]; + cache = tmp2; + cache_len = len - i - 1; + } else { + [self freeMemory: cache]; + cache = NULL; + cache_len = 0; + } + + return ret; + } + } + + /* There was no newline or \0 */ cache = [self resizeMemory: cache toSize: cache_len + len]; - } @catch (OFException *e) { - [self freeMemory: tmp]; - @throw e; - } - - /* - * It's possible that cache_len + len is 0 and thus cache was - * set to NULL by resizeMemory:toSize:. - */ - if (cache != NULL) - memcpy(cache + cache_len, tmp, len); - - cache_len += len; - } + + /* + * It's possible that cache_len + len is 0 and thus + * cache was set to NULL by resizeMemory:toSize:. + */ + if (cache != NULL) + memcpy(cache + cache_len, tmp, len); + + cache_len += len; + } + } @finally { + [self freeMemory: tmp]; + } + + /* Get rid of a warning, never reached anyway */ + assert(0); } - (size_t)writeNBytes: (size_t)size fromBuffer: (const char*)buf { Index: src/OFXMLElement.h ================================================================== --- src/OFXMLElement.h +++ src/OFXMLElement.h @@ -7,152 +7,14 @@ * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ -#import "OFObject.h" #import "OFString.h" -#import "OFDictionary.h" -#import "OFArray.h" extern int _OFXMLElement_reference; -/** - * The OFXMLAttribute represents an attribute of an XML element as an object. - */ -@interface OFXMLAttribute: OFObject -{ - OFString *prefix; - OFString *name; - OFString *ns; - OFString *value; -} - -/** - * \param name The name of the attribute - * \param prefix The prefix of the attribute - * \param ns The namespace of the attribute - * \param value The string value of the attribute - * \return A new autoreleased OFXMLAttribute with the specified parameters - */ -+ attributeWithName: (OFString*)name - prefix: (OFString*)prefix - namespace: (OFString*)ns - stringValue: (OFString*)value; - -/** - * Initializes an already allocated OFXMLAttribute. - * - * \param name The name of the attribute - * \param prefix The prefix of the attribute - * \param ns The namespace of the attribute - * \param value The string value of the attribute - * \return An initialized OFXMLAttribute with the specified parameters - */ -- initWithName: (OFString*)name - prefix: (OFString*)prefix - namespace: (OFString*)ns - stringValue: (OFString*)value; - -/** - * \return The name of the attribute as an autoreleased OFString - */ -- (OFString*)name; - -/** - * \return The prefix of the attribute as an autoreleased OFString - */ -- (OFString*)prefix; - -/** - * \return The namespace of the attribute as an autoreleased OFString - */ -- (OFString*)namespace; - -/** - * \return The string value of the attribute as an autoreleased OFString - */ -- (OFString*)stringValue; -@end - -/** - * The OFXMLElement represents an XML element as an object which can be - * modified and converted back to XML again. - */ -@interface OFXMLElement: OFObject -{ - OFString *name; - OFArray *attrs; - OFString *stringval; - OFArray *children; -} - -/** - * \param name The name for the element - * \return A new autorelease OFXMLElement with the specified element name - */ -+ elementWithName: (OFString*)name; - -/** - * \param name The name for the element - * \param stringval The value for the element - * \return A new autorelease OFXMLElement with the specified element name and - * value - */ -+ elementWithName: (OFString*)name - stringValue: (OFString*)stringval; - -/** - * Initializes an already allocated OFXMLElement with the specified name. - * - * \param name The name for the element - * \return An initialized OFXMLElement with the specified element name - */ -- initWithName: (OFString*)name; - -/** - * Initializes an already allocated OFXMLElement with the specified name and - * value. - * - * \param name The name for the element - * \param stringval The value for the element - * \return An initialized OFXMLElement with the specified element name and - * value - */ -- initWithName: (OFString*)name - stringValue: (OFString*)stringval; - -/** - * \return A new autoreleased OFString representing the OFXMLElement as an - * XML string - */ -- (OFString*)string; - -/** - * Adds the specified attribute. - * - * \param attr The attribute to add - */ -- addAttribute: (OFXMLAttribute*)attr; - -/** - * Adds the specified attribute with the specified value. - * - * \param name The name of the attribute - * \param value The value of the attribute - */ -- addAttributeWithName: (OFString*)name - stringValue: (OFString*)value; - -/** - * Adds a child to the OFXMLElement. - * - * \param child Another OFXMLElement which is added as a child - */ -- addChild: (OFXMLElement*)child; -@end - /** * The OFString (OFXMLEscaping) category provides an easy way to escape strings * for use in an XML document. */ @interface OFString (OFXMLEscaping) Index: src/OFXMLElement.m ================================================================== --- src/OFXMLElement.m +++ src/OFXMLElement.m @@ -14,264 +14,14 @@ #include #include #include #import "OFXMLElement.h" -#import "OFAutoreleasePool.h" #import "OFExceptions.h" int _OFXMLElement_reference; -@implementation OFXMLAttribute -+ attributeWithName: (OFString*)name_ - prefix: (OFString*)prefix_ - namespace: (OFString*)ns_ - stringValue: (OFString*)value_ -{ - return [[[self alloc] initWithName: name_ - prefix: prefix_ - namespace: ns_ - stringValue: value_] autorelease]; -} - -- initWithName: (OFString*)name_ - prefix: (OFString*)prefix_ - namespace: (OFString*)ns_ - stringValue: (OFString*)value_ -{ - self = [super init]; - - name = [name_ copy]; - prefix = [prefix_ copy]; - ns = [ns_ copy]; - value = [value_ copy]; - - return self; -} - -- (void)dealloc -{ - [name release]; - [prefix release]; - [ns release]; - [value release]; - - [super dealloc]; -} - -- (OFString*)name -{ - return [[name copy] autorelease]; -} - -- (OFString*)prefix -{ - return [[prefix copy] autorelease]; -} - -- (OFString*)namespace -{ - return [[ns copy] autorelease]; -} - -- (OFString*)stringValue -{ - return [[value copy] autorelease]; -} -@end - -@implementation OFXMLElement -+ elementWithName: (OFString*)name_ -{ - return [[[self alloc] initWithName: name_] autorelease]; -} - -+ elementWithName: (OFString*)name_ - stringValue: (OFString*)stringval_ -{ - return [[[self alloc] initWithName: name_ - stringValue: stringval_] autorelease]; -} - -- init -{ - @throw [OFNotImplementedException newWithClass: isa - selector: _cmd]; -} - -- initWithName: (OFString*)name_ -{ - self = [super init]; - - name = [name_ copy]; - - return self; -} - -- initWithName: (OFString*)name_ - stringValue: (OFString*)stringval_ -{ - self = [super init]; - - name = [name_ copy]; - stringval = [stringval_ copy]; - - return self; -} - -- (OFString*)string -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - char *str_c; - size_t len, i, j, attrs_count; - OFXMLAttribute **attrs_carray; - OFString *ret, *tmp; - - len = [name cStringLength] + 3; - str_c = [self allocMemoryWithSize: len]; - - /* Start of tag */ - *str_c = '<'; - memcpy(str_c + 1, [name cString], [name cStringLength]); - i = [name cStringLength] + 1; - - /* Attributes */ - attrs_carray = [attrs cArray]; - attrs_count = [attrs count]; - - for (j = 0; j < attrs_count; j++) { - /* FIXME: Add namespace support */ - OFString *attr_name = [attrs_carray[j] name]; - tmp = [[attrs_carray[j] stringValue] stringByXMLEscaping]; - - len += [attr_name cStringLength] + [tmp cStringLength] + 4; - @try { - str_c = [self resizeMemory: str_c - toSize: len]; - } @catch (OFException *e) { - [self freeMemory: str_c]; - @throw e; - } - - str_c[i++] = ' '; - memcpy(str_c + i, [attr_name cString], - [attr_name cStringLength]); - i += [attr_name cStringLength]; - str_c[i++] = '='; - str_c[i++] = '\''; - memcpy(str_c + i, [tmp cString], [tmp cStringLength]); - i += [tmp cStringLength]; - str_c[i++] = '\''; - - [pool releaseObjects]; - } - - /* Childen */ - if (stringval != nil || children != nil) { - if (stringval != nil) - tmp = [stringval stringByXMLEscaping]; - else if (children != nil) { - OFXMLElement **children_carray = [children cArray]; - size_t children_count = [children count]; - IMP append; - - tmp = [OFMutableString string]; - append = [tmp methodForSelector: - @selector(appendCStringWithoutUTF8Checking:)]; - - for (j = 0; j < children_count; j++) - append(tmp, @selector( - appendCStringWithoutUTF8Checking:), - [[children_carray[j] string] cString]); - } - - len += [tmp cStringLength] + [name cStringLength] + 2; - @try { - str_c = [self resizeMemory: str_c - toSize: len]; - } @catch (OFException *e) { - [self freeMemory: str_c]; - @throw e; - } - - str_c[i++] = '>'; - memcpy(str_c + i, [tmp cString], [tmp cStringLength]); - i += [tmp cStringLength]; - str_c[i++] = '<'; - str_c[i++] = '/'; - memcpy(str_c + i, [name cString], [name cStringLength]); - i += [name cStringLength]; - } else - str_c[i++] = '/'; - - str_c[i++] = '>'; - assert(i == len); - - [pool release]; - - @try { - ret = [OFString stringWithCString: str_c - length: len]; - } @finally { - [self freeMemory: str_c]; - } - return ret; -} - -- addAttribute: (OFXMLAttribute*)attr -{ - if (attrs == nil) - attrs = [[OFMutableArray alloc] init]; - - /* FIXME: Prevent having it twice! */ - - [attrs addObject: attr]; - - return self; -} - -- addAttributeWithName: (OFString*)name_ - stringValue: (OFString*)value -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - [self addAttribute: [OFXMLAttribute attributeWithName: name_ - prefix: nil - namespace: nil - stringValue: value]]; - [pool release]; - - return self; -} - -/* TODO: Replace attribute */ -/* TODO: Remove attribute */ - -- addChild: (OFXMLElement*)child -{ - if (stringval != nil) - @throw [OFInvalidArgumentException newWithClass: isa - selector: _cmd]; - - if (children == nil) - children = [[OFMutableArray alloc] init]; - - [children addObject: child]; - - return self; -} - -- (void)dealloc -{ - [name release]; - [attrs release]; - [stringval release]; - [children release]; - - [super dealloc]; -} -@end - @implementation OFString (OFXMLEscaping) - stringByXMLEscaping { char *str_c, *append, *tmp; size_t len, append_len; Index: src/OFXMLParser.h ================================================================== --- src/OFXMLParser.h +++ src/OFXMLParser.h @@ -7,81 +7,14 @@ * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ -#import "OFObject.h" #import "OFString.h" extern int _OFXMLParser_reference; -@class OFXMLParser; - -/** - * A protocol that needs to be implemented by delegates for OFXMLParser. - */ -@protocol OFXMLParserDelegate -/** - * This callback is called when the XML parser found the start of a new tag. - * - * \param parser The parser which found a new tag - * \param name The name of the tag which just started - * \param prefix The prefix of the tag which just started or nil - * \param ns The namespace of the tag which just started or nil - * \param attrs The attributes included in the tag which just started or nil - */ -- (void)xmlParser: (OFXMLParser*)parser - didStartTagWithName: (OFString*)name - prefix: (OFString*)prefix - namespace: (OFString*)ns - attributes: (OFArray*)attrs; - -/** - * This callback is called when the XML parser found the end of a tag. - * - * \param parser The parser which found the end of a tag - * \param name The name of the tag which just ended - * \param prefix The prefix of the tag which just ended or nil - * \param ns The namespace of the tag which just ended or nil - */ -- (void)xmlParser: (OFXMLParser*)parser - didEndTagWithName: (OFString*)name - prefix: (OFString*)prefix - namespace: (OFString*)ns; - -/** - * This callback is called when the XML parser found a string. - * - * \param parser The parser which found a string - * \param string The string the XML parser found - */ -- (void)xmlParser: (OFXMLParser*)parser - foundString: (OFString*)string; - -/** - * This callback is called when the XML parser found a comment. - * - * \param parser The parser which found a comment - * \param comment The comment the XML parser found - */ -- (void)xmlParser: (OFXMLParser*)parser - foundComment: (OFString*)comment; - -/** - * This callback is called when the XML parser found an entity it doesn't know. - * The callback is supposed to return a substitution for the entity or nil if - * it is not known to the callback as well, in which case an exception will be - * risen. - * - * \param parser The parser which found an unknown entity - * \param entity The name of the entity the XML parser didn't know - * \return A substitution for the entity or nil - */ -- (OFString*)xmlParser: (OFXMLParser*)parser - foundUnknownEntityNamed: (OFString*)entity; -@end - /** * A protocol that needs to be implemented by delegates for * stringByXMLUnescapingWithHandler:. */ @protocol OFXMLUnescapingDelegate @@ -95,71 +28,10 @@ * \return A substitution for the entity or nil */ - (OFString*)foundUnknownEntityNamed: (OFString*)entity; @end -/** - * An event-based XML parser which calls the delegate's callbacks as soon as - * it finds something, thus suitable for streams as well. - */ -@interface OFXMLParser: OFObject -{ - OFObject *delegate; - enum { - OF_XMLPARSER_OUTSIDE_TAG, - OF_XMLPARSER_TAG_OPENED, - OF_XMLPARSER_IN_TAG_NAME, - OF_XMLPARSER_IN_CLOSE_TAG_NAME, - OF_XMLPARSER_IN_TAG, - OF_XMLPARSER_IN_ATTR_NAME, - OF_XMLPARSER_EXPECT_DELIM, - OF_XMLPARSER_IN_ATTR_VALUE, - OF_XMLPARSER_EXPECT_CLOSE, - OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE, - OF_XMLPARSER_IN_COMMENT_1, - OF_XMLPARSER_IN_COMMENT_2, - OF_XMLPARSER_IN_COMMENT_3, - OF_XMLPARSER_IN_COMMENT_4 - } state; - OFString *cache; - OFString *name; - OFString *prefix; - OFString *ns; - OFArray *attrs; - OFString *attr_name; - OFString *attr_prefix; - char delim; - OFArray *previous; -} - -/** - * \return A new, autoreleased OFXMLParser - */ -+ xmlParser; - -/** - * \return The delegate that is used by the XML parser - */ -- (id)delegate; - -/** - * Sets the delegate the OFXMLParser should use. - * - * \param delegate The delegate to use - */ -- setDelegate: (OFObject *)delegate; - -/** - * Parses a buffer with the specified size. - * - * \param buf The buffer to parse - * \param size The size of the buffer - */ -- parseBuffer: (const char*)buf - withSize: (size_t)size; -@end - /** * The OFString (OFXMLUnescaping) category provides methods to unescape XML in * strings. */ @interface OFString (OFXMLUnescaping) @@ -174,8 +46,5 @@ * * \param h An OFXMLUnescapingDelegate as a handler for unknown entities */ - stringByXMLUnescapingWithHandler: (OFObject *)h; @end - -@interface OFObject (OFXMLParserDelegate) -@end Index: src/OFXMLParser.m ================================================================== --- src/OFXMLParser.m +++ src/OFXMLParser.m @@ -18,19 +18,10 @@ #import "OFExceptions.h" #import "OFMacros.h" int _OFXMLParser_reference; -static OF_INLINE OFString* -transform_string(OFString *cache, OFObject *handler) -{ - /* TODO: Support for xml:space */ - - [cache removeLeadingAndTrailingWhitespaces]; - return [cache stringByXMLUnescapingWithHandler: handler]; -} - static OF_INLINE OFString* parse_numeric_entity(const char *entity, size_t length) { of_unichar_t c; size_t i; @@ -75,430 +66,10 @@ return [OFString stringWithCString: buf length: i]; } -@implementation OFXMLParser -+ xmlParser -{ - return [[[self alloc] init] autorelease]; -} - -- init -{ - self = [super init]; - - @try { - cache = [[OFMutableString alloc] init]; - previous = [[OFMutableArray alloc] init]; - } @catch (OFException *e) { - /* We can't use [super dealloc] on OS X here. Compiler bug? */ - [self dealloc]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [delegate release]; - - [cache release]; - [name release]; - [prefix release]; - [ns release]; - [attrs release]; - [attr_name release]; - [attr_prefix release]; - [previous release]; - - [super dealloc]; -} - -- (id)delegate -{ - return [[delegate retain] autorelease]; -} - -- setDelegate: (OFObject *)delegate_ -{ - [delegate_ retain]; - [delegate release]; - delegate = delegate_; - - return self; -} - -- parseBuffer: (const char*)buf - withSize: (size_t)size -{ - OFAutoreleasePool *pool; - size_t i, last, len; - - last = 0; - - for (i = 0; i < size; i++) { - switch (state) { - /* Not in a tag */ - case OF_XMLPARSER_OUTSIDE_TAG: - if (buf[i] == '<') { - len = i - last; - - if (len > 0) - [cache appendCString: buf + last - withLength: len]; - - if ([cache cStringLength] > 0) { - OFString *str; - - pool = [[OFAutoreleasePool alloc] init]; - str = transform_string(cache, self); - [delegate xmlParser: self - foundString: str]; - [pool release]; - } - - [cache setToCString: ""]; - - last = i + 1; - state = OF_XMLPARSER_TAG_OPENED; - } - break; - - /* Tag was just opened */ - case OF_XMLPARSER_TAG_OPENED: - if (buf[i] == '/') { - last = i + 1; - state = OF_XMLPARSER_IN_CLOSE_TAG_NAME; - } else if(buf[i] == '!') { - last = i + 1; - state = OF_XMLPARSER_IN_COMMENT_1; - } else { - state = OF_XMLPARSER_IN_TAG_NAME; - i--; - } - break; - - /* Inside a tag, no name yet */ - case OF_XMLPARSER_IN_TAG_NAME: - if (buf[i] == ' ' || buf[i] == '>' || buf[i] == '/') { - const char *cache_c, *tmp; - size_t cache_len; - - len = i - last; - if (len > 0) - [cache appendCString: buf + last - withLength: len]; - cache_c = [cache cString]; - cache_len = [cache cStringLength]; - - if ((tmp = memchr(cache_c, ':', - cache_len)) != NULL) { - name = [[OFString alloc] - initWithCString: tmp + 1 - length: cache_len - (tmp - - cache_c) - 1]; - prefix = [[OFString alloc] - initWithCString: cache_c - length: tmp - cache_c]; - } else { - name = [cache copy]; - prefix = nil; - } - - if (buf[i] == '>' || buf[i] == '/') { - pool = [[OFAutoreleasePool alloc] init]; - - [delegate xmlParser: self - didStartTagWithName: name - prefix: prefix - namespace: ns - attributes: nil]; - - if (buf[i] == '/') - [delegate xmlParser: self - didEndTagWithName: name - prefix: prefix - namespace: ns]; - else - [previous addObject: - [[cache copy] autorelease]]; - - [pool release]; - - [name release]; - [prefix release]; - [ns release]; - name = prefix = ns = nil; - - state = (buf[i] == '/' - ? OF_XMLPARSER_EXPECT_CLOSE - : OF_XMLPARSER_OUTSIDE_TAG); - } else - state = OF_XMLPARSER_IN_TAG; - - [cache setToCString: ""]; - last = i + 1; - } - break; - - /* Inside a close tag, no name yet */ - case OF_XMLPARSER_IN_CLOSE_TAG_NAME: - if (buf[i] == ' ' || buf[i] == '>') { - const char *cache_c, *tmp; - size_t cache_len; - - len = i - last; - if (len > 0) - [cache appendCString: buf + last - withLength: len]; - cache_c = [cache cString]; - cache_len = [cache cStringLength]; - - if ((tmp = memchr(cache_c, ':', - cache_len)) != NULL) { - name = [[OFString alloc] - initWithCString: tmp + 1 - length: cache_len - (tmp - - cache_c) - 1]; - prefix = [[OFString alloc] - initWithCString: cache_c - length: tmp - cache_c]; - } else { - name = [cache copy]; - prefix = nil; - } - - if (![[previous lastObject] isEqual: cache]) - @throw [OFMalformedXMLException - newWithClass: isa]; - [previous removeNObjects: 1]; - - [cache setToCString: ""]; - - pool = [[OFAutoreleasePool alloc] init]; - - [delegate xmlParser: self - didEndTagWithName: name - prefix: prefix - namespace: ns]; - - [pool release]; - - [name release]; - [prefix release]; - [ns release]; - name = prefix = ns = nil; - - last = i + 1; - state = (buf[i] == ' ' - ? OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE - : OF_XMLPARSER_OUTSIDE_TAG); - } - break; - - /* Inside a tag, name found */ - case OF_XMLPARSER_IN_TAG: - if (buf[i] == '>' || buf[i] == '/') { - pool = [[OFAutoreleasePool alloc] init]; - - [delegate xmlParser: self - didStartTagWithName: name - prefix: prefix - namespace: ns - attributes: attrs]; - - if (buf[i] == '/') - [delegate xmlParser: self - didEndTagWithName: name - prefix: prefix - namespace: ns]; - else if (prefix != nil) { - OFString *str = [OFString - stringWithFormat: @"%s:%s", - [prefix cString], - [name cString]]; - [previous addObject: str]; - } else - [previous addObject: name]; - - [pool release]; - - [name release]; - [prefix release]; - [ns release]; - [attrs release]; - name = prefix = ns = nil; - attrs = nil; - - last = i + 1; - state = (buf[i] == '/' - ? OF_XMLPARSER_EXPECT_CLOSE - : OF_XMLPARSER_OUTSIDE_TAG); - } else if (buf[i] != ' ') { - last = i; - state = OF_XMLPARSER_IN_ATTR_NAME; - i--; - } - break; - - /* Looking for attribute name */ - case OF_XMLPARSER_IN_ATTR_NAME: - if (buf[i] == '=') { - const char *cache_c, *tmp; - size_t cache_len; - - len = i - last; - if (len > 0) - [cache appendCString: buf + last - withLength: len]; - - cache_c = [cache cString]; - cache_len = [cache cStringLength]; - - if ((tmp = memchr(cache_c, ':', - cache_len)) != NULL ) { - attr_name = [[OFString alloc] - initWithCString: tmp + 1 - length: cache_len - (tmp - - cache_c) - 1]; - attr_prefix = [[OFString alloc] - initWithCString: cache_c - length: tmp - cache_c]; - } else { - attr_name = [cache copy]; - attr_prefix = nil; - } - - [cache setToCString: ""]; - - last = i + 1; - state = OF_XMLPARSER_EXPECT_DELIM; - } - break; - - /* Expecting delimiter */ - case OF_XMLPARSER_EXPECT_DELIM: - if (buf[i] != '\'' && buf[i] != '"') - @throw [OFMalformedXMLException - newWithClass: isa]; - - delim = buf[i]; - last = i + 1; - state = OF_XMLPARSER_IN_ATTR_VALUE; - break; - - /* Looking for attribute value */ - case OF_XMLPARSER_IN_ATTR_VALUE: - if (buf[i] == delim) { - OFString *attr_val; - - len = i - last; - if (len > 0) - [cache appendCString: buf + last - withLength: len]; - - if (attrs == nil) - attrs = [[OFMutableArray alloc] init]; - - pool = [[OFAutoreleasePool alloc] init]; - attr_val = [cache - stringByXMLUnescapingWithHandler: self]; - [attrs addObject: [OFXMLAttribute - attributeWithName: attr_name - prefix: attr_prefix - namespace: nil - stringValue: attr_val]]; - [pool release]; - - [cache setToCString: ""]; - [attr_name release]; - [attr_prefix release]; - attr_name = attr_prefix = nil; - - last = i + 1; - state = OF_XMLPARSER_IN_TAG; - } - break; - - /* Expecting closing '>' */ - case OF_XMLPARSER_EXPECT_CLOSE: - if (buf[i] == '>') { - last = i + 1; - state = OF_XMLPARSER_OUTSIDE_TAG; - } else - @throw [OFMalformedXMLException - newWithClass: isa]; - break; - - /* Expecting closing '>' or space */ - case OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE: - if (buf[i] == '>') { - last = i + 1; - state = OF_XMLPARSER_OUTSIDE_TAG; - } else if (buf[i] != ' ') - @throw [OFMalformedXMLException - newWithClass: isa]; - break; - - /* Comment */ - case OF_XMLPARSER_IN_COMMENT_1: - case OF_XMLPARSER_IN_COMMENT_2: - if (buf[i] != '-') - @throw [OFMalformedXMLException - newWithClass: isa]; - last = i + 1; - state++; - break; - case OF_XMLPARSER_IN_COMMENT_3: - if (buf[i] == '-') - state = OF_XMLPARSER_IN_COMMENT_4; - break; - case OF_XMLPARSER_IN_COMMENT_4: - if (buf[i] == '-') { - size_t cache_len; - - [cache appendCString: buf + last - withLength: i - last]; - cache_len = [cache length]; - - pool = [[OFAutoreleasePool alloc] init]; - [cache removeCharactersFromIndex: cache_len - 1 - toIndex: cache_len]; - [cache removeLeadingAndTrailingWhitespaces]; - [delegate xmlParser: self - foundComment: cache]; - [pool release]; - - [cache setToCString: ""]; - - last = i + 1; - state = OF_XMLPARSER_EXPECT_CLOSE; - } else - state = OF_XMLPARSER_IN_COMMENT_3; - - break; - } - } - - len = size - last; - /* In OF_XMLPARSER_IN_TAG, there can be only spaces */ - if (len > 0 && state != OF_XMLPARSER_IN_TAG) - [cache appendCString: buf + last - withLength: len]; - - return self; -} - -- (OFString*)foundUnknownEntityNamed: (OFString*)entity -{ - return [delegate xmlParser: self - foundUnknownEntityNamed: entity]; -} -@end - @implementation OFString (OFXMLUnescaping) - stringByXMLUnescaping { return [self stringByXMLUnescapingWithHandler: nil]; } @@ -584,39 +155,6 @@ [ret appendCStringWithoutUTF8Checking: string + last length: i - last]; return ret; } -@end - -@implementation OFObject (OFXMLParserDelegate) -- (void)xmlParser: (OFXMLParser*)parser - didStartTagWithName: (OFString*)name - prefix: (OFString*)prefix - namespace: (OFString*)ns - attributes: (OFArray*)attrs -{ -} - -- (void)xmlParser: (OFXMLParser*)parser - didEndTagWithName: (OFString*)name - prefix: (OFString*)prefix - namespace: (OFString*)ns -{ -} - -- (void)xmlParser: (OFXMLParser*)parser - foundString: (OFString*)string -{ -} - -- (void)xmlParser: (OFXMLParser*)parser - foundComment: (OFString*)comment -{ -} - -- (OFString*)xmlParser: (OFXMLParser*)parser - foundUnknownEntityNamed: (OFString*)entity -{ - return nil; -} @end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -11,12 +11,10 @@ OFObject.m \ ${OFPLUGIN_M} \ OFString.m \ OFTCPSocket.m \ ${OFTHREAD_M} \ - OFXMLElement.m \ - OFXMLParser.m \ main.m IPHONE_USER = mobile IPHONE_TMP = /tmp/objfw-test DELETED tests/OFXMLElement.m Index: tests/OFXMLElement.m ================================================================== --- tests/OFXMLElement.m +++ tests/OFXMLElement.m @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2008 - 2009 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE included in - * the packaging of this file. - */ - -#include "config.h" - -#import "OFXMLElement.h" -#import "OFAutoreleasePool.h" -#import "OFString.h" -#import "OFExceptions.h" - -#import "main.h" - -static OFString *module = @"OFXMLElement"; - -void -xmlelement_tests() -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - OFXMLElement *elem[2]; - - TEST(@"+[elementWithName:]", - (elem[0] = [OFXMLElement elementWithName: @"foo"]) && - [[elem[0] string] isEqual: @""]) - - TEST(@"+[elementWithName:stringValue:]", - (elem[1] = [OFXMLElement elementWithName: @"foo" - stringValue: @"b&ar"]) && - [[elem[1] string] isEqual: @"b&ar"]) - - TEST(@"-[addAttributeWithName:stringValue:]", - [elem[0] addAttributeWithName: @"foo" - stringValue: @"b&ar"] && - [[elem[0] string] isEqual: @""] && - [elem[1] addAttributeWithName: @"foo" - stringValue: @"b&ar"] && - [[elem[1] string] isEqual: @"b&ar"]) - - TEST(@"-[addChild:]", - [elem[0] addChild: [OFXMLElement elementWithName: @"bar"]] && - [[elem[0] string] isEqual: @""]) - - [pool drain]; -} DELETED tests/OFXMLParser.m Index: tests/OFXMLParser.m ================================================================== --- tests/OFXMLParser.m +++ tests/OFXMLParser.m @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2008 - 2009 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE included in - * the packaging of this file. - */ - -#include "config.h" - -#include - -#import "OFXMLParser.h" -#import "OFAutoreleasePool.h" -#import "OFString.h" -#import "OFExceptions.h" - -#import "main.h" - -static OFString *module = @"OFXMLParser"; -static int i = 0; - -enum event_type { - TAG_START, - TAG_END, - STRING, - COMMENT -}; - -static void -callback(enum event_type et, OFString *name, OFString *prefix, OFString *ns, - OFArray *attrs, OFString *string, OFString *comment) -{ - OFString *msg; - id *carray; - size_t count; - - i++; - msg = [OFString stringWithFormat: @"Parsing part #%d", i]; - - switch (i) { - case 1: - case 5: - TEST(msg, et == STRING && [string isEqual: @"bar"]) - break; - case 2: - /* FIXME: Namespace */ - carray = [attrs cArray]; - count = [attrs count]; - - TEST(msg, et == TAG_START && [name isEqual: @"bar"] && - [prefix isEqual: @"foo"] && ns == nil && - attrs != nil && count == 2 && - /* Attribute 1 */ - [[carray[0] name] isEqual: @"bar"] && - [carray[0] prefix] == nil && - [[carray[0] stringValue] isEqual: @"b&az"] && - [carray[0] namespace] == nil && - /* Attribute 2 */ - [[carray[1] name] isEqual: @"qux"] && - [[carray[1] prefix] isEqual: @"qux"] && - [[carray[1] stringValue] isEqual: @" quux "] && - [carray[1] namespace] == nil) - break; - case 3: - TEST(msg, et == STRING && [string isEqual: @"foo\r\n" - "foo<barbar quxbar\r\n" - ""; - size_t j, len; - - TEST(@"+[xmlParser]", (parser = [OFXMLParser xmlParser])) - - TEST(@"-[setDelegate:]", - [parser setDelegate: [[[ParserDelegate alloc] init] autorelease]]) - - /* Simulate a stream where we only get chunks */ - len = strlen(str); - - for (j = 0; j < len; j+= 2) { - if (j + 2 > len) - [parser parseBuffer: str + j - withSize: 1]; - else - [parser parseBuffer: str + j - withSize: 2]; - } - - TEST(@"Checking if everything was parsed", i == 11) - - [pool drain]; -} Index: tests/main.m ================================================================== --- tests/main.m +++ tests/main.m @@ -31,12 +31,10 @@ extern void string_tests(); extern void tcpsocket_tests(); #ifdef OF_THREADS extern void thread_tests(); #endif -extern void xmlelement_tests(); -extern void xmlparser_tests(); static int fails = 0; static void output(OFString *str, int color) @@ -108,13 +106,11 @@ list_tests(); tcpsocket_tests(); #ifdef OF_THREADS thread_tests(); #endif - xmlelement_tests(); - xmlparser_tests(); #ifdef OF_PLUGINS plugin_tests(); #endif return fails; }