Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -24,11 +24,12 @@ OFStream.m \ OFString.m \ OFTCPSocket.m \ OFThread.m \ OFURLEncoding.m \ - OFXMLElement.m + OFXMLElement.m \ + OFXMLParser.m INCLUDESTMP := ${SRCS:.c=.h} INCLUDES := ${INCLUDESTMP:.m=.h} \ OFMacros.h \ asprintf.h \ Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -201,5 +201,6 @@ #import "OFConstString.h" #import "OFMutableString.h" #import "OFURLEncoding.h" #import "OFXMLElement.h" +#import "OFXMLParser.h" Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -22,12 +22,10 @@ #define madvise(addr, len, advise) #endif #import "OFString.h" #import "OFAutoreleasePool.h" -#import "OFURLEncoding.h" -#import "OFXMLElement.h" #import "OFExceptions.h" #import "OFMacros.h" #import "asprintf.h" @@ -34,10 +32,11 @@ /* References for static linking */ void _references_to_categories_of_OFString() { _OFURLEncoding_reference = 1; _OFXMLElement_reference = 1; + _OFXMLParser_reference = 1; }; int of_string_check_utf8(const char *str, size_t len) { ADDED src/OFXMLParser.h Index: src/OFXMLParser.h ================================================================== --- src/OFXMLParser.h +++ src/OFXMLParser.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2008 - 2009 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. 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" + +extern int _OFXMLParser_reference; + +@class OFXMLParser; + +@protocol OFXMLParserDelegate +- (BOOL)xmlParser: (OFXMLParser*)parser + didStartTagWithName: (OFString*)name + andPrefix: (OFString*)prefix + andNamespace: (OFString*)ns + andAttributes: (OFDictionary*)attrs; +- (BOOL)xmlParser: (OFXMLParser*)parser + didEndTagWithName: (OFString*)name + andPrefix: (OFString*)prefix + andNamespace: (OFString*)ns; +- (BOOL)xmlParser: (OFXMLParser*)parser + foundString: (OFString*)string; +@end + +@interface OFXMLParser: OFObject +{ + OFObject *delegate; + int state; +} + ++ xmlParser; +- (id)delegate; +- setDelegate: (OFObject *)delegate; +@end + +@protocol OFXMLUnescapingDelegate +- (OFString*)foundUnknownEntityNamed: (OFString*)entitiy; +@end + +@interface OFString (OFXMLUnescaping) +- stringByXMLUnescaping; +- stringByXMLUnescapingWithHandler: (OFObject *)h; +@end ADDED src/OFXMLParser.m Index: src/OFXMLParser.m ================================================================== --- src/OFXMLParser.m +++ src/OFXMLParser.m @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2008 - 2009 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. 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 "OFExceptions.h" + +int _OFXMLParser_reference; + +@implementation OFXMLParser ++ xmlParser +{ + return [[[self alloc] init] autorelease]; +} + +- (id)delegate +{ + return [[delegate retain] autorelease]; +} + +- setDelegate: (OFObject *)delegate_ +{ + [delegate release]; + delegate = [delegate_ retain]; + + return self; +} +@end + +@implementation OFString (OFXMLUnescaping) +- stringByXMLUnescaping +{ + return [self stringByXMLUnescapingWithHandler: nil]; +} + +- stringByXMLUnescapingWithHandler: (OFObject *)h +{ + size_t i, last; + BOOL in_entity; + OFString *ret; + + last = 0; + in_entity = NO; + ret = [OFMutableString string]; + + for (i = 0; i < length; i++) { + if (!in_entity && string[i] == '&') { + [ret appendCStringWithoutUTF8Checking: string + last + andLength: i - last]; + + last = i + 1; + in_entity = YES; + } else if (in_entity && string[i] == ';') { + size_t len = i - last; + + if (len == 2 && !memcmp(string + last, "lt", 2)) + [ret appendString: @"<"]; + else if (len == 2 && !memcmp(string + last, "gt", 2)) + [ret appendString: @">"]; + else if (len == 4 && !memcmp(string + last, "quot", 4)) + [ret appendString: @"\""]; + else if (len == 4 && !memcmp(string + last, "apos", 4)) + [ret appendString: @"'"]; + else if (len == 3 && !memcmp(string + last, "amp", 3)) + [ret appendString: @"&"]; + else if (h != nil) { + OFAutoreleasePool *pool; + OFString *n, *tmp; + + pool = [[OFAutoreleasePool alloc] init]; + + n = [OFString stringWithCString: string + last + andLength: len]; + tmp = [h foundUnknownEntityNamed: n]; + + if (tmp == nil) + @throw [OFInvalidEncodingException + newWithClass: isa]; + + [ret appendString: tmp]; + [pool release]; + } else + @throw [OFInvalidEncodingException + newWithClass: isa]; + + last = i + 1; + in_entity = NO; + } + } + + if (in_entity) + @throw [OFInvalidEncodingException newWithClass: isa]; + + [ret appendCStringWithoutUTF8Checking: string + last + andLength: i - last]; + + return ret; +} +@end Index: tests/OFString/OFString.m ================================================================== --- tests/OFString/OFString.m +++ tests/OFString/OFString.m @@ -22,11 +22,11 @@ #define ZD "%zd" #else #define ZD "%u" #endif -#define NUM_TESTS 49 +#define NUM_TESTS 53 #define SUCCESS \ printf("\r\033[1;%dmTests successful: " ZD "/%d\033[0m", \ (i == NUM_TESTS - 1 ? 32 : 33), i + 1, NUM_TESTS); \ fflush(stdout); #define FAIL \ @@ -47,10 +47,23 @@ } @catch (exception *e) { \ SUCCESS \ } \ i++; +@interface EntityHandler: OFObject +@end + +@implementation EntityHandler +- (OFString*)foundUnknownEntityNamed: (OFString*)entity +{ + if ([entity isEqual: @"foo"]) + return @"bar"; + + return nil; +} +@end + int main() { size_t i = 0; size_t j = 0; @@ -59,10 +72,11 @@ OFString *s1 = [OFMutableString stringWithCString: "test"]; OFString *s2 = [OFMutableString stringWithCString: ""]; OFString *s3; OFString *s4 = [OFMutableString string]; OFArray *a; + EntityHandler *h; s3 = [s1 copy]; CHECK([s1 isEqual: s3]) CHECK(![s1 isEqual: [[OFObject alloc] init]]) @@ -174,9 +188,21 @@ /* XML escaping tests */ s1 = [@" &world'\"!&" stringByXMLEscaping]; CHECK([s1 isEqual: @"<hello> &world'"!&"]) + /* XML unescaping tests */ + CHECK([[s1 stringByXMLUnescaping] isEqual: @" &world'\"!&"]); + CHECK_EXCEPT([@"&foo;" stringByXMLUnescaping], + OFInvalidEncodingException) + + h = [[EntityHandler alloc] init]; + s1 = [@"x&foo;y" stringByXMLUnescapingWithHandler: h]; + CHECK([s1 isEqual: @"xbary"]); + + CHECK_EXCEPT([@"x&" stringByXMLUnescaping], + OFInvalidEncodingException) + puts(""); return 0; }