Artifact 874124744727dbe9fdb492487ae314e8539f800b365d35e53065cf68cb0581a5:
- File
src/OFXMLParser.m
— part of check-in
[e1e7ffa903]
at
2011-09-22 23:25:42
on branch trunk
— Exceptions are now autoreleased.
This is safe as an "exception loop" can't happen, since if allocating
an exception fails, it throws an OFAllocFailedException which is
preallocated and can always be thrown.So, the worst case would be that an autorelease of an exception fails,
triggering an OFOutOfMemoryException for which there is no memory,
resulting in an OFAllocFailedException to be thrown. (user: js, size: 25324) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011 * Jonathan Schleifer <js@webkeks.org> * * 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.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #define OF_XML_PARSER_M #include <string.h> #include <sys/types.h> #import "OFXMLParser.h" #import "OFString.h" #import "OFArray.h" #import "OFDictionary.h" #import "OFDataArray.h" #import "OFXMLAttribute.h" #import "OFStream.h" #import "OFFile.h" #import "OFAutoreleasePool.h" #import "OFInitializationFailedException.h" #import "OFMalformedXMLException.h" #import "OFUnboundNamespaceException.h" #import "macros.h" typedef void (*state_function)(id, SEL, const char*, size_t*, size_t*); static SEL selectors[OF_XMLPARSER_NUM_STATES]; static state_function lookupTable[OF_XMLPARSER_NUM_STATES]; static OF_INLINE void cache_append(OFDataArray *cache, const char *string, of_string_encoding_t encoding, size_t length) { if (OF_LIKELY(encoding == OF_STRING_ENCODING_UTF_8)) [cache addNItems: length fromCArray: string]; else { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFString *tmp = [OFString stringWithCString: string encoding: encoding length: length]; [cache addNItems: [tmp UTF8StringLength] fromCArray: [tmp UTF8String]]; [pool release]; } } static OFString* transform_string(OFDataArray *cache, size_t cut, BOOL unescape, OFObject <OFStringXMLUnescapingDelegate> *delegate) { OFMutableString *ret = [OFMutableString stringWithUTF8String: [cache cArray] length: [cache count]]; [ret replaceOccurrencesOfString: @"\r\n" withString: @"\n"]; [ret replaceOccurrencesOfString: @"\r" withString: @"\n"]; if (cut > 0) { size_t length = [ret length]; [ret deleteCharactersInRange: of_range(length - cut, cut)]; } if (unescape) return [ret stringByXMLUnescapingWithDelegate: delegate]; [ret makeImmutable]; return ret; } static OFString* namespace_for_prefix(OFString *prefix, OFArray *namespaces) { OFDictionary **cArray = [namespaces cArray]; ssize_t i; if (prefix == nil) prefix = @""; for (i = [namespaces count] - 1; i >= 0; i--) { OFString *tmp; if ((tmp = [cArray[i] objectForKey: prefix]) != nil) return tmp; } return nil; } static OF_INLINE void resolve_attribute_namespace(OFXMLAttribute *attribute, OFArray *namespaces, Class isa) { OFString *attributeNS; OFString *attributePrefix = attribute->ns; if (attributePrefix == nil) return; attributeNS = namespace_for_prefix(attributePrefix, namespaces); if ((attributePrefix != nil && attributeNS == nil)) @throw [OFUnboundNamespaceException exceptionWithClass: isa prefix: attributePrefix]; [attribute->ns release]; attribute->ns = [attributeNS retain]; } @implementation OFXMLParser + (void)initialize { size_t i; const SEL selectors_[] = { @selector(_parseOutsideTagWithBuffer:i:last:), @selector(_parseTagOpenedWithBuffer:i:last:), @selector(_parseInProcessingInstructionsWithBuffer:i:last:), @selector(_parseInTagNameWithBuffer:i:last:), @selector(_parseInCloseTagNameWithBuffer:i:last:), @selector(_parseInTagWithBuffer:i:last:), @selector(_parseInAttributeNameWithBuffer:i:last:), @selector(_parseExpectDelimiterWithBuffer:i:last:), @selector(_parseInAttributeValueWithBuffer:i:last:), @selector(_parseExpectCloseWithBuffer:i:last:), @selector(_parseExpectSpaceOrCloseWithBuffer:i:last:), @selector(_parseInExclamationMarkWithBuffer:i:last:), @selector(_parseInCDATAOpeningWithBuffer:i:last:), @selector(_parseInCDATA1WithBuffer:i:last:), @selector(_parseInCDATA2WithBuffer:i:last:), @selector(_parseInCommentOpeningWithBuffer:i:last:), @selector(_parseInComment1WithBuffer:i:last:), @selector(_parseInComment2WithBuffer:i:last:), @selector(_parseInDoctypeWithBuffer:i:last:), }; memcpy(selectors, selectors_, sizeof(selectors_)); for (i = 0; i < OF_XMLPARSER_NUM_STATES; i++) { if (![self instancesRespondToSelector: selectors[i]]) @throw [OFInitializationFailedException exceptionWithClass: self]; lookupTable[i] = (state_function) [self instanceMethodForSelector: selectors[i]]; } } + parser { return [[[self alloc] init] autorelease]; } - init { self = [super init]; @try { OFAutoreleasePool *pool; OFMutableDictionary *dict; cache = [[OFBigDataArray alloc] init]; previous = [[OFMutableArray alloc] init]; namespaces = [[OFMutableArray alloc] init]; pool = [[OFAutoreleasePool alloc] init]; dict = [OFMutableDictionary dictionaryWithKeysAndObjects: @"xml", @"http://www.w3.org/XML/1998/namespace", @"xmlns", @"http://www.w3.org/2000/xmlns/", nil]; [namespaces addObject: dict]; acceptProlog = YES; lineNumber = 1; encoding = OF_STRING_ENCODING_UTF_8; [pool release]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [(id)delegate release]; [cache release]; [name release]; [prefix release]; [namespaces release]; [attributes release]; [attributeName release]; [attributePrefix release]; [previous release]; [super dealloc]; } - (id <OFXMLParserDelegate>)delegate { OF_GETTER(delegate, YES) } - (void)setDelegate: (id <OFXMLParserDelegate>)delegate_ { OF_SETTER(delegate, delegate_, YES, NO) } - (void)parseBuffer: (const char*)buffer withLength: (size_t)length { size_t i, last = 0; for (i = 0; i < length; i++) { size_t j = i; lookupTable[state](self, selectors[state], buffer, &i, &last); /* Ensure we don't count this character twice */ if (i != j) continue; if (buffer[i] == '\r' || (buffer[i] == '\n' && !lastCarriageReturn)) lineNumber++; lastCarriageReturn = (buffer[i] == '\r'); } /* In OF_XMLPARSER_IN_TAG, there can be only spaces */ if (length - last > 0 && state != OF_XMLPARSER_IN_TAG) cache_append(cache, buffer + last, encoding, length - last); } - (void)parseString: (OFString*)string { [self parseBuffer: [string UTF8String] withLength: [string UTF8StringLength]]; } - (void)parseStream: (OFStream*)stream { char *buffer = [self allocMemoryWithSize: of_pagesize]; @try { while (![stream isAtEndOfStream]) { size_t length = [stream readNBytes: of_pagesize intoBuffer: buffer]; [self parseBuffer: buffer withLength: length]; } } @finally { [self freeMemory: buffer]; } } - (void)parseFile: (OFString*)path { OFFile *file = [[OFFile alloc] initWithPath: path mode: @"rb"]; @try { [self parseStream: file]; } @finally { [file release]; } } /* * The following methods handle the different states of the parser. They are * looked up in +[initialize] and put in a lookup table to speed things up. * One dispatch for every character would be way too slow! */ /* Not in a tag */ - (void)_parseOutsideTagWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { size_t length; if ((finishedParsing || [previous count] < 1) && buffer[*i] != ' ' && buffer[*i] != '\t' && buffer[*i] != '\n' && buffer[*i] != '\r' && buffer[*i] != '\f' && buffer[*i] != '<') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; if (buffer[*i] != '<') return; if ((length = *i - *last) > 0) cache_append(cache, buffer + *last, encoding, length); if ([cache count] > 0) { OFString *characters; OFAutoreleasePool *pool; pool = [[OFAutoreleasePool alloc] init]; characters = transform_string(cache, 0, YES, self); [delegate parser: self foundCharacters: characters]; [pool release]; } [cache removeNItems: [cache count]]; *last = *i + 1; state = OF_XMLPARSER_TAG_OPENED; } /* Tag was just opened */ - (void)_parseTagOpenedWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (finishedParsing && buffer[*i] != '!' && buffer[*i] != '?') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; switch (buffer[*i]) { case '?': *last = *i + 1; state = OF_XMLPARSER_IN_PROCESSING_INSTRUCTIONS; level = 0; break; case '/': *last = *i + 1; state = OF_XMLPARSER_IN_CLOSE_TAG_NAME; acceptProlog = NO; break; case '!': *last = *i + 1; state = OF_XMLPARSER_IN_EXCLAMATIONMARK; acceptProlog = NO; break; default: state = OF_XMLPARSER_IN_TAG_NAME; acceptProlog = NO; (*i)--; break; } } /* <?xml […]?> */ - (BOOL)_parseXMLProcessingInstructions: (OFString*)pi { const char *cString; size_t i, last, length; int piState = 0; OFString *attribute = nil; OFMutableString *value = nil; char piDelimiter = 0; if (!acceptProlog) return NO; acceptProlog = NO; pi = [pi substringWithRange: of_range(3, [pi length] - 3)]; pi = [pi stringByDeletingEnclosingWhitespaces]; cString = [pi UTF8String]; length = [pi UTF8StringLength]; for (i = last = 0; i < length; i++) { switch (piState) { case 0: if (cString[i] == ' ' || cString[i] == '\t' || cString[i] == '\r' || cString[i] == '\n' || cString[i] == '\f') continue; last = i; piState = 1; i--; break; case 1: if (cString[i] != '=') continue; attribute = [OFString stringWithUTF8String: cString + last length: i - last]; last = i + 1; piState = 2; break; case 2: if (cString[i] != '\'' && cString[i] != '"') return NO; piDelimiter = cString[i]; last = i + 1; piState = 3; break; case 3: if (cString[i] != piDelimiter) continue; value = [OFMutableString stringWithUTF8String: cString + last length: i - last]; if ([attribute isEqual: @"version"]) if (![value hasPrefix: @"1."]) return NO; if ([attribute isEqual: @"encoding"]) { [value lower]; if ([value isEqual: @"utf-8"]) encoding = OF_STRING_ENCODING_UTF_8; else if ([value isEqual: @"iso-8859-1"]) encoding = OF_STRING_ENCODING_ISO_8859_1; else if ([value isEqual: @"iso-8859-15"]) encoding = OF_STRING_ENCODING_ISO_8859_15; else if ([value isEqual: @"windows-1252"]) encoding = OF_STRING_ENCODING_WINDOWS_1252; else return NO; } last = i + 1; piState = 0; break; } } if (piState != 0) return NO; return YES; } /* Inside processing instructions */ - (void)_parseInProcessingInstructionsWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] == '?') level = 1; else if (level == 1 && buffer[*i] == '>') { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFString *pi; cache_append(cache, buffer + *last, encoding, *i - *last); pi = transform_string(cache, 1, NO, nil); if ([pi isEqual: @"xml"] || [pi hasPrefix: @"xml "] || [pi hasPrefix: @"xml\t"] || [pi hasPrefix: @"xml\r"] || [pi hasPrefix: @"xml\n"]) if (![self _parseXMLProcessingInstructions: pi]) @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; [delegate parser: self foundProcessingInstructions: pi]; [pool release]; [cache removeNItems: [cache count]]; *last = *i + 1; state = OF_XMLPARSER_OUTSIDE_TAG; } else level = 0; } /* Inside a tag, no name yet */ - (void)_parseInTagNameWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; const char *cacheCString, *tmp; size_t length, cacheLength; OFString *cacheString; if (buffer[*i] != ' ' && buffer[*i] != '\t' && buffer[*i] != '\n' && buffer[*i] != '\r' && buffer[*i] != '\f' && buffer[*i] != '>' && buffer[*i] != '/') return; if ((length = *i - *last) > 0) cache_append(cache, buffer + *last, encoding, length); pool = [[OFAutoreleasePool alloc] init]; cacheCString = [cache cArray]; cacheLength = [cache count]; cacheString = [OFString stringWithUTF8String: cacheCString length: cacheLength]; if ((tmp = memchr(cacheCString, ':', cacheLength)) != NULL) { name = [[OFString alloc] initWithUTF8String: tmp + 1 length: cacheLength - (tmp - cacheCString) - 1]; prefix = [[OFString alloc] initWithUTF8String: cacheCString length: tmp - cacheCString]; } else { name = [cacheString copy]; prefix = nil; } if (buffer[*i] == '>' || buffer[*i] == '/') { OFAutoreleasePool *pool2; OFString *ns; ns = namespace_for_prefix(prefix, namespaces); if (prefix != nil && ns == nil) @throw [OFUnboundNamespaceException exceptionWithClass: isa prefix: prefix]; pool2 = [[OFAutoreleasePool alloc] init]; [delegate parser: self didStartElement: name withPrefix: prefix namespace: ns attributes: nil]; if (buffer[*i] == '/') { [delegate parser: self didEndElement: name withPrefix: prefix namespace: ns]; if ([previous count] == 0) finishedParsing = YES; } else [previous addObject: cacheString]; [pool2 release]; [name release]; [prefix release]; name = prefix = nil; state = (buffer[*i] == '/' ? OF_XMLPARSER_EXPECT_CLOSE : OF_XMLPARSER_OUTSIDE_TAG); } else state = OF_XMLPARSER_IN_TAG; [pool release]; if (buffer[*i] != '/') { pool = [[OFAutoreleasePool alloc] init]; [namespaces addObject: [OFMutableDictionary dictionary]]; [pool release]; } [cache removeNItems: [cache count]]; *last = *i + 1; } /* Inside a close tag, no name yet */ - (void)_parseInCloseTagNameWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; const char *cacheCString, *tmp; size_t length, cacheLength; OFString *cacheString; OFString *ns; if (buffer[*i] != ' ' && buffer[*i] != '\t' && buffer[*i] != '\n' && buffer[*i] != '\r' && buffer[*i] != '\f' && buffer[*i] != '>') return; if ((length = *i - *last) > 0) cache_append(cache, buffer + *last, encoding, length); pool = [[OFAutoreleasePool alloc] init]; cacheCString = [cache cArray]; cacheLength = [cache count]; cacheString = [OFString stringWithUTF8String: cacheCString length: cacheLength]; if ((tmp = memchr(cacheCString, ':', cacheLength)) != NULL) { name = [[OFString alloc] initWithUTF8String: tmp + 1 length: cacheLength - (tmp - cacheCString) - 1]; prefix = [[OFString alloc] initWithUTF8String: cacheCString length: tmp - cacheCString]; } else { name = [cacheString copy]; prefix = nil; } if (![[previous lastObject] isEqual: cacheString]) @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; [previous removeLastObject]; [cache removeNItems: [cache count]]; ns = namespace_for_prefix(prefix, namespaces); if (prefix != nil && ns == nil) @throw [OFUnboundNamespaceException exceptionWithClass: isa prefix: prefix]; [delegate parser: self didEndElement: name withPrefix: prefix namespace: ns]; [pool release]; [namespaces removeLastObject]; [name release]; [prefix release]; name = prefix = nil; *last = *i + 1; state = (buffer[*i] == '>' ? OF_XMLPARSER_OUTSIDE_TAG : OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE); if ([previous count] == 0) finishedParsing = YES; } /* Inside a tag, name found */ - (void)_parseInTagWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; OFString *ns; OFXMLAttribute **attributesCArray; size_t j, attributesCount; if (buffer[*i] != '>' && buffer[*i] != '/') { if (buffer[*i] != ' ' && buffer[*i] != '\t' && buffer[*i] != '\n' && buffer[*i] != '\r' && buffer[*i] != '\f') { *last = *i; state = OF_XMLPARSER_IN_ATTR_NAME; (*i)--; } return; } attributesCArray = [attributes cArray]; attributesCount = [attributes count]; ns = namespace_for_prefix(prefix, namespaces); if (prefix != nil && ns == nil) @throw [OFUnboundNamespaceException exceptionWithClass: isa prefix: prefix]; for (j = 0; j < attributesCount; j++) resolve_attribute_namespace(attributesCArray[j], namespaces, isa); pool = [[OFAutoreleasePool alloc] init]; [delegate parser: self didStartElement: name withPrefix: prefix namespace: ns attributes: attributes]; if (buffer[*i] == '/') { [delegate parser: self didEndElement: name withPrefix: prefix namespace: ns]; if ([previous count] == 0) finishedParsing = YES; [namespaces removeLastObject]; } else if (prefix != nil) { OFString *str = [OFString stringWithFormat: @"%@:%@", prefix, name]; [previous addObject: str]; } else [previous addObject: name]; [pool release]; [name release]; [prefix release]; [attributes release]; name = prefix = nil; attributes = nil; *last = *i + 1; state = (buffer[*i] == '/' ? OF_XMLPARSER_EXPECT_CLOSE : OF_XMLPARSER_OUTSIDE_TAG); } /* Looking for attribute name */ - (void)_parseInAttributeNameWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; OFMutableString *cacheString; const char *cacheCString, *tmp; size_t length, cacheLength; if (buffer[*i] != '=') return; if ((length = *i - *last) > 0) cache_append(cache, buffer + *last, encoding, length); pool = [[OFAutoreleasePool alloc] init]; cacheString = [OFMutableString stringWithUTF8String: [cache cArray] length: [cache count]]; [cacheString deleteEnclosingWhitespaces]; /* Prevent a useless copy later */ [cacheString makeImmutable]; cacheCString = [cacheString UTF8String]; cacheLength = [cacheString UTF8StringLength]; if ((tmp = memchr(cacheCString, ':', cacheLength)) != NULL) { attributeName = [[OFString alloc] initWithUTF8String: tmp + 1 length: cacheLength - (tmp - cacheCString) - 1]; attributePrefix = [[OFString alloc] initWithUTF8String: cacheCString length: tmp - cacheCString]; } else { attributeName = [cacheString copy]; attributePrefix = nil; } [pool release]; [cache removeNItems: [cache count]]; *last = *i + 1; state = OF_XMLPARSER_EXPECT_DELIM; } /* Expecting delimiter */ - (void)_parseExpectDelimiterWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { *last = *i + 1; if (buffer[*i] == ' ' || buffer[*i] == '\t' || buffer[*i] == '\n' || buffer[*i] == '\r' || buffer[*i] == '\f') return; if (buffer[*i] != '\'' && buffer[*i] != '"') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; delimiter = buffer[*i]; state = OF_XMLPARSER_IN_ATTR_VALUE; } /* Looking for attribute value */ - (void)_parseInAttributeValueWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; OFString *attributeValue; size_t length; if (buffer[*i] != delimiter) return; if ((length = *i - *last) > 0) cache_append(cache, buffer + *last, encoding, length); pool = [[OFAutoreleasePool alloc] init]; attributeValue = transform_string(cache, 0, YES, self); if (attributePrefix == nil && [attributeName isEqual: @"xmlns"]) [[namespaces lastObject] setObject: attributeValue forKey: @""]; if ([attributePrefix isEqual: @"xmlns"]) [[namespaces lastObject] setObject: attributeValue forKey: attributeName]; if (attributes == nil) attributes = [[OFMutableArray alloc] init]; [attributes addObject: [OFXMLAttribute attributeWithName: attributeName namespace: attributePrefix stringValue: attributeValue]]; [pool release]; [cache removeNItems: [cache count]]; [attributeName release]; [attributePrefix release]; attributeName = attributePrefix = nil; *last = *i + 1; state = OF_XMLPARSER_IN_TAG; } /* Expecting closing '>' */ - (void)_parseExpectCloseWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] == '>') { *last = *i + 1; state = OF_XMLPARSER_OUTSIDE_TAG; } else @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; } /* Expecting closing '>' or space */ - (void)_parseExpectSpaceOrCloseWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] == '>') { *last = *i + 1; state = OF_XMLPARSER_OUTSIDE_TAG; } else if (buffer[*i] != ' ' && buffer[*i] != '\t' && buffer[*i] != '\n' && buffer[*i] != '\r' && buffer[*i] != '\f') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; } /* In <! */ - (void)_parseInExclamationMarkWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (finishedParsing && buffer[*i] != '-') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; if (buffer[*i] == '-') state = OF_XMLPARSER_IN_COMMENT_OPENING; else if (buffer[*i] == '[') { state = OF_XMLPARSER_IN_CDATA_OPENING; level = 0; } else if (buffer[*i] == 'D') { state = OF_XMLPARSER_IN_DOCTYPE; level = 0; } else @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; *last = *i + 1; } /* CDATA */ - (void)_parseInCDATAOpeningWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] != "CDATA["[level]) @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; if (++level == 6) { state = OF_XMLPARSER_IN_CDATA_1; level = 0; } *last = *i + 1; } - (void)_parseInCDATA1WithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] == ']') level++; else level = 0; if (level == 2) state = OF_XMLPARSER_IN_CDATA_2; } - (void)_parseInCDATA2WithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; OFString *CDATA; if (buffer[*i] != '>') { state = OF_XMLPARSER_IN_CDATA_1; level = (buffer[*i] == ']' ? 1 : 0); return; } pool = [[OFAutoreleasePool alloc] init]; cache_append(cache, buffer + *last, encoding, *i - *last); CDATA = transform_string(cache, 2, NO, nil); [delegate parser: self foundCDATA: CDATA]; [pool release]; [cache removeNItems: [cache count]]; *last = *i + 1; state = OF_XMLPARSER_OUTSIDE_TAG; } /* Comment */ - (void)_parseInCommentOpeningWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] != '-') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; *last = *i + 1; state = OF_XMLPARSER_IN_COMMENT_1; level = 0; } - (void)_parseInComment1WithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if (buffer[*i] == '-') level++; else level = 0; if (level == 2) state = OF_XMLPARSER_IN_COMMENT_2; } - (void)_parseInComment2WithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { OFAutoreleasePool *pool; OFString *comment; if (buffer[*i] != '>') @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; pool = [[OFAutoreleasePool alloc] init]; cache_append(cache, buffer + *last, encoding, *i - *last); comment = transform_string(cache, 2, NO, nil); [delegate parser: self foundComment: comment]; [pool release]; [cache removeNItems: [cache count]]; *last = *i + 1; state = OF_XMLPARSER_OUTSIDE_TAG; } /* In <!DOCTYPE ...> */ - (void)_parseInDoctypeWithBuffer: (const char*)buffer i: (size_t*)i last: (size_t*)last { if ((level < 6 && buffer[*i] != "OCTYPE"[level]) || (level == 6 && buffer[*i] != ' ' && buffer[*i] != '\t' && buffer[*i] != '\n' && buffer[*i] != '\r' && buffer[*i] != '\f')) @throw [OFMalformedXMLException exceptionWithClass: isa parser: self]; if (level < 7 || buffer[*i] == '<') level++; if (buffer[*i] == '>') { if (level == 7) state = OF_XMLPARSER_OUTSIDE_TAG; else level--; } *last = *i + 1; } - (size_t)lineNumber { return lineNumber; } - (BOOL)finishedParsing { return finishedParsing; } - (OFString*)string: (OFString*)string containsUnknownEntityNamed: (OFString*)entity { return [delegate parser: self foundUnknownEntityNamed: entity]; } @end @implementation OFObject (OFXMLParserDelegate) - (void)parser: (OFXMLParser*)parser foundProcessingInstructions: (OFString*)pi { } - (void)parser: (OFXMLParser*)parser didStartElement: (OFString*)name withPrefix: (OFString*)prefix namespace: (OFString*)ns attributes: (OFArray*)attributes { } - (void)parser: (OFXMLParser*)parser didEndElement: (OFString*)name withPrefix: (OFString*)prefix namespace: (OFString*)ns { } - (void)parser: (OFXMLParser*)parser foundCharacters: (OFString*)characters { } - (void)parser: (OFXMLParser*)parser foundCDATA: (OFString*)CDATA { } - (void)parser: (OFXMLParser*)parser foundComment: (OFString*)comment { } - (OFString*)parser: (OFXMLParser*)parser foundUnknownEntityNamed: (OFString*)entity { return nil; } @end