Index: src/OFString+XMLUnescaping.h ================================================================== --- src/OFString+XMLUnescaping.h +++ src/OFString+XMLUnescaping.h @@ -11,10 +11,15 @@ #import "OFString.h" extern int _OFString_XMLUnescaping_reference; +#ifdef OF_HAVE_BLOCKS +typedef OFString* (^of_string_xml_unescaping_block_t)(OFString *str, + OFString *entity); +#endif + /** * \brief A protocol that needs to be implemented by delegates for * -[stringByXMLUnescapingWithHandler:]. */ @protocol OFStringXMLUnescapingDelegate @@ -39,13 +44,22 @@ * Unescapes XML in the string. */ - (OFString*)stringByXMLUnescaping; /** - * Unescapes XML in the string and uses the specified handler for unknown + * Unescapes XML in the string and uses the specified delegate for unknown * entities. * * \param h An OFXMLUnescapingDelegate as a handler for unknown entities */ - (OFString*)stringByXMLUnescapingWithDelegate: (id )delegate; + +/** + * Unescapes XML in the string and uses the specified block for unknown + * entities. + * + * \param h A block as a handler for unknown entities + */ +- (OFString*)stringByXMLUnescapingWithBlock: + (of_string_xml_unescaping_block_t)block; @end Index: src/OFString+XMLUnescaping.m ================================================================== --- src/OFString+XMLUnescaping.m +++ src/OFString+XMLUnescaping.m @@ -157,6 +157,91 @@ [ret appendCStringWithoutUTF8Checking: string + last length: i - last]; return ret; } + +#ifdef OF_HAVE_BLOCKS +- (OFString*)stringByXMLUnescapingWithBlock: + (of_string_xml_unescaping_block_t)block +{ + size_t i, last; + BOOL in_entity; + OFMutableString *ret; + + last = 0; + in_entity = NO; + ret = [OFMutableString string]; + ((OFString*)ret)->isUTF8 = [self isUTF8]; + + for (i = 0; i < length; i++) { + if (!in_entity && string[i] == '&') { + [ret appendCStringWithoutUTF8Checking: string + last + length: i - last]; + + last = i + 1; + in_entity = YES; + } else if (in_entity && string[i] == ';') { + char *entity = string + last; + size_t len = i - last; + + if (len == 2 && !memcmp(entity, "lt", 2)) + [ret appendCStringWithoutUTF8Checking: "<" + length: 1]; + else if (len == 2 && !memcmp(entity, "gt", 2)) + [ret appendCStringWithoutUTF8Checking: ">" + length: 1]; + else if (len == 4 && !memcmp(entity, "quot", 4)) + [ret appendCStringWithoutUTF8Checking: "\"" + length: 1]; + else if (len == 4 && !memcmp(entity, "apos", 4)) + [ret appendCStringWithoutUTF8Checking: "'" + length: 1]; + else if (len == 3 && !memcmp(entity, "amp", 3)) + [ret appendCStringWithoutUTF8Checking: "&" + length: 1]; + else if (entity[0] == '#') { + OFAutoreleasePool *pool; + OFString *tmp; + + pool = [[OFAutoreleasePool alloc] init]; + tmp = parse_numeric_entity(entity, len); + + if (tmp == nil) + @throw [OFInvalidEncodingException + newWithClass: isa]; + + [ret appendString: tmp]; + [pool release]; + } else { + OFAutoreleasePool *pool; + OFString *n, *tmp; + + pool = [[OFAutoreleasePool alloc] init]; + + n = [OFString stringWithCString: entity + length: len]; + tmp = block(self, n); + + if (tmp == nil) + @throw [OFInvalidEncodingException + newWithClass: isa]; + + [ret appendString: tmp]; + [pool release]; + } + + last = i + 1; + in_entity = NO; + } + } + + if (in_entity) + @throw [OFInvalidEncodingException newWithClass: isa]; + + [ret appendCStringWithoutUTF8Checking: string + last + length: i - last]; + + return ret; +} +#endif @end Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -364,13 +364,24 @@ EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " @"#5", OFInvalidEncodingException, [@"&#g;" stringByXMLUnescaping]) EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " @"#6", OFInvalidEncodingException, [@"&#xg;" stringByXMLUnescaping]) - TEST(@"-[stringByXMLUnescapingWithHandler:]", + TEST(@"-[stringByXMLUnescapingWithDelegate:]", (h = [[[EntityHandler alloc] init] autorelease]) && [[@"x&foo;y" stringByXMLUnescapingWithDelegate: h] isEqual: @"xbary"]) + +#ifdef OF_HAVE_BLOCKS + TEST(@"-[stringByXMLUnescapingWithBlock:]", + [[@"x&foo;y" stringByXMLUnescapingWithBlock: + ^ OFString* (OFString *str, OFString *entity) { + if ([entity isEqual: @"foo"]) + return @"bar"; + + return nil; + }] isEqual: @"xbary"]) +#endif [pool drain]; } @end