2004-10-20 10:48:04 +00:00
|
|
|
|
/** Implementation for NSXMLParser for GNUStep
|
|
|
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
|
Date: May 2004
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2009-03-20 18:52:59 +00:00
|
|
|
|
SloppyParser additions based on code by Nikolaus Schaller
|
|
|
|
|
|
2004-10-20 10:48:04 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2004-10-20 10:48:04 +00:00
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
2004-10-20 10:48:04 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2004-10-20 10:48:04 +00:00
|
|
|
|
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
|
2019-12-09 23:36:00 +00:00
|
|
|
|
Lesser General Public License for more details.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2004-10-20 10:48:04 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2024-11-07 13:37:59 +00:00
|
|
|
|
Software Foundation, Inc., 31 Milk Street #960789 Boston, MA 02196 USA.
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
|
*/
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
|
#import "common.h"
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#define EXPOSE_NSXMLParser_IVARS 1
|
|
|
|
|
#import "Foundation/NSArray.h"
|
|
|
|
|
#import "Foundation/NSError.h"
|
|
|
|
|
#import "Foundation/NSEnumerator.h"
|
|
|
|
|
#import "Foundation/NSException.h"
|
|
|
|
|
#import "Foundation/NSXMLParser.h"
|
|
|
|
|
#import "Foundation/NSData.h"
|
|
|
|
|
#import "Foundation/NSDictionary.h"
|
|
|
|
|
#import "Foundation/NSNull.h"
|
2010-05-21 17:40:43 +00:00
|
|
|
|
#import "GNUstepBase/GSMime.h"
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2010-05-29 06:42:38 +00:00
|
|
|
|
@interface GSMimeDocument (internal)
|
|
|
|
|
+ (NSString*) charsetForXml: (NSData*)xml;
|
|
|
|
|
@end
|
|
|
|
|
|
2008-01-27 08:57:12 +00:00
|
|
|
|
static NSNull *null = nil;
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* We always have the native (sloppy) parser, and can get that behavior
|
|
|
|
|
* by using the GSSloppyXMLParser class.
|
|
|
|
|
* With libxml2 we have a stricter parser (which will break on some OSX
|
|
|
|
|
* property lists) available using GSStrictXMLParser.
|
2007-11-05 11:30:23 +00:00
|
|
|
|
*/
|
|
|
|
|
@interface GSSloppyXMLParser : NSXMLParser
|
|
|
|
|
@end
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@implementation GSSloppyXMLParser
|
|
|
|
|
@end
|
|
|
|
|
@interface GSStrictXMLParser : NSXMLParser
|
|
|
|
|
@end
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@implementation NSString (NSXMLParser)
|
|
|
|
|
|
|
|
|
|
- (NSString *) _stringByExpandingXMLEntities
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSMutableString *t = [NSMutableString stringWithString: self];
|
|
|
|
|
|
|
|
|
|
[t replaceOccurrencesOfString: @"&"
|
|
|
|
|
withString: @"&"
|
|
|
|
|
options: 0
|
|
|
|
|
range: NSMakeRange(0, [t length])]; // must be first!
|
|
|
|
|
[t replaceOccurrencesOfString: @"<"
|
|
|
|
|
withString: @"<"
|
|
|
|
|
options: 0
|
|
|
|
|
range: NSMakeRange(0, [t length])];
|
|
|
|
|
[t replaceOccurrencesOfString: @">"
|
|
|
|
|
withString: @">"
|
|
|
|
|
options: 0
|
|
|
|
|
range: NSMakeRange(0, [t length])];
|
|
|
|
|
[t replaceOccurrencesOfString: @"\""
|
|
|
|
|
withString: @"""
|
|
|
|
|
options: 0
|
|
|
|
|
range: NSMakeRange(0, [t length])];
|
|
|
|
|
[t replaceOccurrencesOfString: @"'"
|
|
|
|
|
withString: @"'"
|
|
|
|
|
options: 0
|
|
|
|
|
range: NSMakeRange(0, [t length])];
|
|
|
|
|
return t;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@end
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
static inline NSString *
|
|
|
|
|
NewUTF8STR(const void *ptr, int len)
|
2009-01-13 20:52:49 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
|
|
s = [[NSString alloc] initWithBytes: ptr
|
|
|
|
|
length: len
|
|
|
|
|
encoding: NSUTF8StringEncoding];
|
|
|
|
|
if (s == nil)
|
|
|
|
|
NSLog(@"could not convert to UTF8 string! bytes=%p len=%d", ptr, len);
|
|
|
|
|
return s;
|
2009-01-13 20:52:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@interface GSXMLParserIvars : NSObject
|
2008-01-27 07:23:53 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@public
|
|
|
|
|
NSMutableArray *tagPath; // hierarchy of tags
|
|
|
|
|
NSMutableArray *namespaces;
|
|
|
|
|
NSMutableDictionary *defaults;
|
|
|
|
|
NSData *data;
|
|
|
|
|
NSError *error;
|
2020-05-08 14:05:17 +00:00
|
|
|
|
const unsigned char *bytes;
|
|
|
|
|
NSUInteger cp; // character position
|
|
|
|
|
NSUInteger cend; // end of data
|
2020-05-06 14:24:04 +00:00
|
|
|
|
int line; // current line (counts from 0)
|
|
|
|
|
int column; // current column (counts from 0)
|
|
|
|
|
BOOL abort; // abort parse loop
|
|
|
|
|
BOOL ignorable; // whitespace is ignorable
|
|
|
|
|
BOOL whitespace; // had only whitespace in current data
|
|
|
|
|
BOOL shouldProcessNamespaces;
|
|
|
|
|
BOOL shouldReportNamespacePrefixes;
|
|
|
|
|
BOOL shouldResolveExternalEntities;
|
|
|
|
|
BOOL acceptHTML; // be lazy with bad tag nesting
|
|
|
|
|
BOOL hasStarted;
|
|
|
|
|
BOOL hasElement;
|
|
|
|
|
IMP didEndElement;
|
|
|
|
|
IMP didEndMappingPrefix;
|
|
|
|
|
IMP didStartElement;
|
|
|
|
|
IMP didStartMappingPrefix;
|
|
|
|
|
IMP foundCDATA;
|
|
|
|
|
IMP foundCharacters;
|
|
|
|
|
IMP foundComment;
|
|
|
|
|
IMP foundIgnorable;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
@implementation GSXMLParserIvars
|
|
|
|
|
- (NSString*) description
|
|
|
|
|
{
|
|
|
|
|
return [[super description] stringByAppendingFormat:
|
|
|
|
|
@" shouldProcessNamespaces: %d"
|
|
|
|
|
@" shouldReportNamespacePrefixes: %d"
|
|
|
|
|
@" shouldResolveExternalEntities: %d"
|
|
|
|
|
@" acceptHTML: %d"
|
|
|
|
|
@" hasStarted: %d"
|
|
|
|
|
@" hasElement: %d",
|
|
|
|
|
shouldProcessNamespaces,
|
|
|
|
|
shouldReportNamespacePrefixes,
|
|
|
|
|
shouldResolveExternalEntities,
|
|
|
|
|
acceptHTML,
|
|
|
|
|
hasStarted,
|
|
|
|
|
hasElement];
|
2008-01-27 07:23:53 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@end
|
2008-01-27 07:23:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
static SEL didEndElementSel = 0;
|
|
|
|
|
static SEL didEndMappingPrefixSel;
|
|
|
|
|
static SEL didStartElementSel;
|
|
|
|
|
static SEL didStartMappingPrefixSel;
|
|
|
|
|
static SEL foundCDATASel;
|
|
|
|
|
static SEL foundCharactersSel;
|
|
|
|
|
static SEL foundCommentSel;
|
|
|
|
|
static SEL foundIgnorableSel;
|
|
|
|
|
|
|
|
|
|
@interface NSXMLParser (Private)
|
|
|
|
|
- (NSString *) _newQarg;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSXMLParser
|
|
|
|
|
|
|
|
|
|
#define EXTRA_DEBUG 0
|
|
|
|
|
|
|
|
|
|
#define _parser (self->_parser)
|
|
|
|
|
#define _handler (self->_handler)
|
|
|
|
|
#define this ((GSXMLParserIvars*)_parser)
|
|
|
|
|
#define _del ((id)_handler)
|
|
|
|
|
|
|
|
|
|
static Class sloppy = Nil;
|
|
|
|
|
static Class strict = Nil;
|
|
|
|
|
|
|
|
|
|
+ (id) allocWithZone: (NSZone*)z
|
2008-01-27 07:23:53 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (self == [NSXMLParser class])
|
2008-01-27 07:23:53 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#if defined(HAVE_LIBXML)
|
|
|
|
|
return NSAllocateObject(strict, 0, z);
|
|
|
|
|
#else
|
|
|
|
|
return NSAllocateObject(sloppy, 0, z);
|
|
|
|
|
#endif
|
2008-01-27 07:23:53 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return NSAllocateObject(self, 0, z);
|
2008-01-27 07:23:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
+ (void) initialize
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
sloppy = [GSSloppyXMLParser class];
|
|
|
|
|
strict = [GSStrictXMLParser class];
|
|
|
|
|
if (null == nil)
|
|
|
|
|
{
|
|
|
|
|
null = RETAIN([NSNull null]);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE([NSObject leakAt: &null]);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
if (didEndElementSel == 0)
|
|
|
|
|
{
|
|
|
|
|
didEndElementSel
|
|
|
|
|
= @selector(parser:didEndElement:namespaceURI:qualifiedName:);
|
|
|
|
|
didEndMappingPrefixSel
|
|
|
|
|
= @selector(parser:didEndMappingPrefix:);
|
|
|
|
|
didStartElementSel
|
|
|
|
|
= @selector(parser:didStartElement:namespaceURI:qualifiedName:attributes:);
|
|
|
|
|
didStartMappingPrefixSel
|
|
|
|
|
= @selector(parser:didStartMappingPrefix:toURI:);
|
|
|
|
|
foundCDATASel
|
|
|
|
|
= @selector(parser:foundCDATA:);
|
|
|
|
|
foundCharactersSel
|
|
|
|
|
= @selector(parser:foundCharacters:);
|
|
|
|
|
foundCommentSel
|
|
|
|
|
= @selector(parser:foundComment:);
|
|
|
|
|
foundIgnorableSel
|
|
|
|
|
= @selector(parser:foundIgnorableWhitespace:);
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
|
|
|
|
|
- (void) abortParsing
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
this->abort = YES;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSInteger) columnNumber
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return this->column;
|
|
|
|
|
}
|
2008-01-27 07:23:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
if (this != 0)
|
2018-01-11 13:50:39 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
RELEASE(this->data);
|
|
|
|
|
RELEASE(this->error);
|
|
|
|
|
RELEASE(this->tagPath);
|
|
|
|
|
RELEASE(this->namespaces);
|
|
|
|
|
RELEASE(this->defaults);
|
|
|
|
|
RELEASE(this);
|
|
|
|
|
_parser = 0;
|
|
|
|
|
_handler = 0;
|
2018-01-11 13:50:39 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
2018-01-11 13:50:39 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) delegate
|
|
|
|
|
{
|
|
|
|
|
return _del;
|
|
|
|
|
}
|
2008-01-27 07:23:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) initWithContentsOfURL: (NSURL *)anURL
|
|
|
|
|
{
|
|
|
|
|
return [self initWithData: [NSData dataWithContentsOfURL: anURL]];
|
|
|
|
|
}
|
2008-01-27 07:23:53 +00:00
|
|
|
|
|
2020-05-08 14:05:17 +00:00
|
|
|
|
#define addr(x) (this->bytes + (x))
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) initWithData: (NSData *)data
|
|
|
|
|
{
|
|
|
|
|
if (data == nil)
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
DESTROY(self);
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
self = [super init];
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
NSStringEncoding enc;
|
2008-01-26 09:23:49 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
_parser = [GSXMLParserIvars new];
|
|
|
|
|
/* Determine character encoding and convert to utf-8 if needed.
|
|
|
|
|
*/
|
|
|
|
|
enc = [GSMimeDocument encodingFromCharset:
|
|
|
|
|
[GSMimeDocument charsetForXml: data]];
|
|
|
|
|
if (enc == NSUTF8StringEncoding
|
|
|
|
|
|| enc == NSASCIIStringEncoding
|
|
|
|
|
|| enc == GSUndefinedEncoding)
|
|
|
|
|
{
|
|
|
|
|
this->data = [data copy];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSString *tmp;
|
2008-01-27 07:23:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
tmp = [[NSString alloc] initWithData: data encoding: enc];
|
|
|
|
|
this->data
|
|
|
|
|
= [[tmp dataUsingEncoding: NSUTF8StringEncoding] retain];
|
|
|
|
|
RELEASE(tmp);
|
|
|
|
|
}
|
|
|
|
|
this->tagPath = [[NSMutableArray alloc] init];
|
|
|
|
|
this->namespaces = [[NSMutableArray alloc] init];
|
2020-05-08 14:05:17 +00:00
|
|
|
|
this->bytes = [this->data bytes];
|
|
|
|
|
this->cp = 0;
|
|
|
|
|
this->cend = [this->data length];
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* If the data contained utf-8 with a BOM, we must skip it.
|
|
|
|
|
*/
|
2020-05-08 14:05:17 +00:00
|
|
|
|
if ((this->cend - this->cp) > 2 && addr(this->cp)[0] == 0xef
|
|
|
|
|
&& addr(this->cp)[1] == 0xbb && addr(this->cp)[2] == 0xbf)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
this->cp += 3; // Skip BOM
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-01-27 07:23:53 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return self;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) initWithStream: (NSInputStream*)stream
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
RELEASE(self); // FIXME
|
2004-10-20 10:48:04 +00:00
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSInteger) lineNumber
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return this->line;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) setDelegate: (id)delegate
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (_handler != delegate)
|
|
|
|
|
{
|
|
|
|
|
_handler = delegate;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([_del respondsToSelector: didEndElementSel])
|
|
|
|
|
{
|
|
|
|
|
this->didEndElement = [_del methodForSelector: didEndElementSel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->didEndElement = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: didEndMappingPrefixSel])
|
|
|
|
|
{
|
|
|
|
|
this->didEndMappingPrefix
|
|
|
|
|
= [_del methodForSelector: didEndMappingPrefixSel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->didEndMappingPrefix = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: didStartElementSel])
|
|
|
|
|
{
|
|
|
|
|
this->didStartElement = [_del methodForSelector: didStartElementSel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->didStartElement = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: didStartMappingPrefixSel])
|
|
|
|
|
{
|
|
|
|
|
this->didStartMappingPrefix
|
|
|
|
|
= [_del methodForSelector: didStartMappingPrefixSel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->didStartMappingPrefix = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: foundCDATASel])
|
|
|
|
|
{
|
|
|
|
|
this->foundCDATA
|
|
|
|
|
= [_del methodForSelector: foundCDATASel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->foundCDATA = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: foundCharactersSel])
|
|
|
|
|
{
|
|
|
|
|
this->foundCharacters
|
|
|
|
|
= [_del methodForSelector: foundCharactersSel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->foundCharacters = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: foundCommentSel])
|
|
|
|
|
{
|
|
|
|
|
this->foundComment
|
|
|
|
|
= [_del methodForSelector: foundCommentSel];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->foundComment = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: foundIgnorableSel])
|
|
|
|
|
{
|
|
|
|
|
/* It seems OX reports ignorable whitespace as characters,
|
|
|
|
|
* so we disable this ... FIXME can this really be right?
|
2004-10-20 10:48:04 +00:00
|
|
|
|
*/
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#if 0
|
|
|
|
|
this->foundIgnorable
|
|
|
|
|
= [_del methodForSelector: foundIgnorableSel];
|
|
|
|
|
#else
|
|
|
|
|
this->foundIgnorable = 0;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this->foundIgnorable = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSError *) parserError
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return this->error;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSArray *) _tagPath
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return this->tagPath;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 14:05:17 +00:00
|
|
|
|
#define cget() (\
|
|
|
|
|
(this->cp < this->cend) \
|
|
|
|
|
? (this->column++, *addr(this->cp++)) \
|
|
|
|
|
: -1)
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) _parseError: (NSString *)message code: (NSInteger)code
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSDictionary *info = nil;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
message = [NSString stringWithFormat: @"line %d, column %d ... %@",
|
|
|
|
|
this->line, this->column, message];
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"XML parseError: %@", message);
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(this->error);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (message != nil)
|
|
|
|
|
{
|
|
|
|
|
info = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
|
|
|
message, NSLocalizedFailureReasonErrorKey, nil];
|
|
|
|
|
}
|
|
|
|
|
this->error = [[NSError alloc] initWithDomain: NSXMLParserErrorDomain
|
|
|
|
|
code: code
|
|
|
|
|
userInfo: info];
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(info);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
this->abort = YES;
|
|
|
|
|
if ([_del respondsToSelector: @selector(parser:parseErrorOccurred:)])
|
|
|
|
|
[_del parser: self parseErrorOccurred: this->error];
|
|
|
|
|
return NO;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* Go up the namespace stack looking for a mapping from p to
|
|
|
|
|
* a URI. Return the first URI found (or an empty string if none is found).
|
2004-10-20 10:48:04 +00:00
|
|
|
|
*/
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString*) _uriForPrefix: (NSString*)p
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
unsigned i = [this->namespaces count];
|
|
|
|
|
NSString *uri = nil;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (uri == nil && i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
id o = [this->namespaces objectAtIndex: i];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (o != (id)null)
|
|
|
|
|
{
|
|
|
|
|
uri = [(NSDictionary*)o objectForKey: p];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (nil == uri) ? @"" : uri;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) _closeLastTag
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSString *tag = [this->tagPath lastObject];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->didEndElement != 0)
|
|
|
|
|
{
|
|
|
|
|
NSString *qualified = tag;
|
|
|
|
|
NSString *uri = @"";
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->shouldProcessNamespaces)
|
|
|
|
|
{
|
|
|
|
|
NSRange r;
|
|
|
|
|
NSString *p = @"";
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
r = [tag rangeOfString: @":" options: NSLiteralSearch];
|
|
|
|
|
if (r.length > 0)
|
|
|
|
|
{
|
|
|
|
|
p = [tag substringToIndex: r.location];
|
|
|
|
|
tag = [tag substringFromIndex: NSMaxRange(r)];
|
|
|
|
|
}
|
|
|
|
|
uri = [self _uriForPrefix: p];
|
|
|
|
|
}
|
|
|
|
|
(*this->didEndElement)(_del,
|
|
|
|
|
didEndElementSel, self, tag, uri, qualified);
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->shouldReportNamespacePrefixes)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->didEndMappingPrefix != 0)
|
|
|
|
|
{
|
|
|
|
|
id d = [this->namespaces lastObject];
|
2008-01-27 08:57:12 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (d != (id)null)
|
|
|
|
|
{
|
|
|
|
|
NSEnumerator *e = [(NSDictionary*)d keyEnumerator];
|
|
|
|
|
NSString *k;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while ((k = [e nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
(*this->didEndMappingPrefix)(_del,
|
|
|
|
|
didEndMappingPrefixSel, self, k);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[this->tagPath removeLastObject];
|
|
|
|
|
[this->namespaces removeLastObject];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) _processDeclaration
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
NSUInteger tp;
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSString *decl;
|
|
|
|
|
NSString *name;
|
|
|
|
|
int c;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (NO == this->hasStarted)
|
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"parserDidStartDocument: ");
|
|
|
|
|
#endif
|
|
|
|
|
this->hasStarted = YES;
|
|
|
|
|
if ([_del respondsToSelector: @selector(parserDidStartDocument:)])
|
|
|
|
|
{
|
|
|
|
|
[_del parserDidStartDocument: self];
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
c = cget();
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
tp = this->cp - 1;
|
|
|
|
|
while (c != EOF && !isspace(c) && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget(); // scan name to delimiting character
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
decl = NewUTF8STR(addr(tp), this->cp - tp - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == decl)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in declaraction"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"decl=%@ - %02x %c", decl, c, isprint(c)?c: ' ');
|
|
|
|
|
#endif
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
tp = this->cp - 1;
|
|
|
|
|
while (c != EOF && !isspace(c) && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget(); // scan name to delimiting character
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
name = NewUTF8STR(addr(tp), this->cp - tp - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == name)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in declaraction name"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(decl);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"name=%@ - %02x %c", name, c, isprint(c)?c: ' ');
|
|
|
|
|
#endif
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([decl isEqualToString: @"ATTLIST"])
|
|
|
|
|
{
|
|
|
|
|
NSMutableDictionary *d;
|
2020-05-08 14:26:07 +00:00
|
|
|
|
NSString *attr;
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSString *type;
|
|
|
|
|
NSString *def;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"_processDeclaration <%@%@ %@>", flag?@"/": @"", decl, name);
|
|
|
|
|
#endif
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* Get the dictionary of attribute defaults for this element.
|
|
|
|
|
*/
|
2020-05-08 14:26:07 +00:00
|
|
|
|
d = [this->defaults objectForKey: name];
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == d)
|
|
|
|
|
{
|
|
|
|
|
if (nil == this->defaults)
|
|
|
|
|
{
|
|
|
|
|
this->defaults = [NSMutableDictionary new];
|
|
|
|
|
}
|
|
|
|
|
d = [NSMutableDictionary new];
|
2020-05-08 14:26:07 +00:00
|
|
|
|
[this->defaults setObject: d forKey: name];
|
|
|
|
|
RELEASE(d);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (c != EOF && c != '>')
|
|
|
|
|
{
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
tp = this->cp - 1;
|
|
|
|
|
while (c != EOF && !isspace(c) && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget(); // scan name to delimiting character
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
attr = NewUTF8STR(addr(tp), this->cp - tp - 1);
|
|
|
|
|
if (nil == attr)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in declaration attr"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(decl);
|
|
|
|
|
RELEASE(name);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#if 1 || EXTRA_DEBUG
|
2020-05-08 14:26:07 +00:00
|
|
|
|
NSLog(@"attr=%@ - %02x %c", attr, c, isprint(c)?c: ' ');
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#endif
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
tp = this->cp - 1;
|
|
|
|
|
while (c != EOF && !isspace(c) && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget(); // scan name to delimiting character
|
|
|
|
|
}
|
2020-05-08 14:05:17 +00:00
|
|
|
|
type = NewUTF8STR(addr(tp), this->cp - tp - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == type)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in declaration type"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
2020-05-08 14:05:17 +00:00
|
|
|
|
RELEASE(name);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(decl);
|
|
|
|
|
RELEASE(attr);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#if 1 || EXTRA_DEBUG
|
|
|
|
|
NSLog(@"type=%@ - %02x %c", type, c, isprint(c)?c: ' ');
|
|
|
|
|
#endif
|
|
|
|
|
/* OSX reports a CDATA type as an empty string.
|
|
|
|
|
*/
|
|
|
|
|
if ([type isEqualToString: @"CDATA"])
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
RELEASE(type);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
type = @"";
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
/* OSX reports a default as nil if it's not a quoted string.
|
|
|
|
|
*/
|
|
|
|
|
if (c == '#')
|
|
|
|
|
{
|
|
|
|
|
while (c != EOF && !isspace(c) && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
def = nil;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
def = [self _newQarg];
|
|
|
|
|
c = cget(); // get character behind qarg value
|
|
|
|
|
}
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* Record default value (if any) for this attribute.
|
|
|
|
|
*/
|
|
|
|
|
if (nil != def)
|
|
|
|
|
{
|
2020-05-08 14:26:07 +00:00
|
|
|
|
[d setObject: def forKey: attr];
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([_del respondsToSelector: @selector(parser:foundAttributeDeclarationWithName:forElement:type:defaultValue:)])
|
|
|
|
|
{
|
|
|
|
|
[_del parser: self
|
2020-05-08 14:26:07 +00:00
|
|
|
|
foundAttributeDeclarationWithName: attr
|
|
|
|
|
forElement: name
|
2020-05-06 14:24:04 +00:00
|
|
|
|
type: type
|
|
|
|
|
defaultValue: def];
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(attr);
|
2020-05-08 14:05:17 +00:00
|
|
|
|
RELEASE(type);
|
|
|
|
|
RELEASE(def);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ([decl isEqualToString: @"DOCTYPE"])
|
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"_processDeclaration <%@%@ %@>", flag?@"/": @"", decl, name);
|
|
|
|
|
#endif
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
while (c != EOF && c != '[' && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
if (c == '[')
|
|
|
|
|
{
|
|
|
|
|
/* Got inline docuent declaration. Scan to ']'
|
|
|
|
|
*/
|
|
|
|
|
while (c != EOF && c != ']')
|
|
|
|
|
{
|
|
|
|
|
if (c == '<')
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
if (c == '!')
|
|
|
|
|
{
|
|
|
|
|
[self _processDeclaration];
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Skip to trailing '>' in DOCTYPE declration.
|
|
|
|
|
*/
|
|
|
|
|
if (c == ']')
|
|
|
|
|
{
|
|
|
|
|
while (c != EOF && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ([decl isEqualToString: @"ELEMENT"])
|
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"_processDeclaration <%@%@ %@>", flag?@"/": @"", decl, name);
|
|
|
|
|
#endif
|
|
|
|
|
while (c != EOF && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
if ([_del respondsToSelector:
|
|
|
|
|
@selector(parser:foundElementDeclarationWithName:model:)])
|
|
|
|
|
{
|
|
|
|
|
[_del parser: self
|
|
|
|
|
foundElementDeclarationWithName: name
|
|
|
|
|
model: @""];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ([decl isEqualToString: @"ENTITY"])
|
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"_processDeclaration <%@%@ %@>", flag?@"/": @"", decl, name);
|
|
|
|
|
#endif
|
|
|
|
|
while (c != EOF && c != '>')
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(decl);
|
|
|
|
|
RELEASE(name);
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) _processTag: (NSString *)tag
|
|
|
|
|
isEnd: (BOOL)flag
|
|
|
|
|
withAttributes: (NSDictionary *)attributes
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->acceptHTML)
|
|
|
|
|
{
|
|
|
|
|
tag = [tag lowercaseString]; // not case sensitive
|
|
|
|
|
}
|
|
|
|
|
if (!flag)
|
|
|
|
|
{
|
|
|
|
|
if (NO == this->hasStarted)
|
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"parserDidStartDocument: ");
|
|
|
|
|
#endif
|
|
|
|
|
this->hasStarted = YES;
|
|
|
|
|
if ([_del respondsToSelector: @selector(parserDidStartDocument:)])
|
|
|
|
|
{
|
|
|
|
|
[_del parserDidStartDocument: self];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ([tag isEqualToString: @"?xml"])
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if ([tag hasPrefix: @"?"])
|
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes);
|
|
|
|
|
#endif
|
|
|
|
|
// parser: foundProcessingInstructionWithTarget: data:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSMutableDictionary *ns = nil;
|
|
|
|
|
NSMutableDictionary *attr = nil;
|
|
|
|
|
NSEnumerator *enumerator = [attributes keyEnumerator];
|
|
|
|
|
NSString *k;
|
|
|
|
|
NSString *uri;
|
|
|
|
|
NSString *qualified;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
this->hasElement = YES;
|
|
|
|
|
while ((k = [enumerator nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
NSString *prefix = nil;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([k isEqualToString: @"xmlns"] == YES)
|
|
|
|
|
{
|
|
|
|
|
prefix = @"";
|
|
|
|
|
}
|
|
|
|
|
else if ([k hasPrefix: @"xmlns:"] == YES)
|
|
|
|
|
{
|
|
|
|
|
prefix = [k substringFromIndex: 6];
|
|
|
|
|
}
|
|
|
|
|
if (prefix != nil)
|
|
|
|
|
{
|
|
|
|
|
if (nil == ns)
|
|
|
|
|
{
|
|
|
|
|
ns = [NSMutableDictionary new];
|
|
|
|
|
if (this->shouldProcessNamespaces)
|
|
|
|
|
{
|
|
|
|
|
attr = [attributes mutableCopy];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
uri = [attributes objectForKey: k];
|
|
|
|
|
[ns setObject: uri forKey: prefix];
|
|
|
|
|
if (attr != nil)
|
|
|
|
|
{
|
|
|
|
|
[attr removeObjectForKey: k];
|
|
|
|
|
}
|
|
|
|
|
if (this->shouldReportNamespacePrefixes)
|
|
|
|
|
{
|
|
|
|
|
if (this->didStartMappingPrefix != 0)
|
|
|
|
|
{
|
|
|
|
|
(*this->didStartMappingPrefix)(_del,
|
|
|
|
|
didStartMappingPrefixSel, self, prefix, uri);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ([attr count] > 0)
|
|
|
|
|
{
|
|
|
|
|
attributes = attr;
|
|
|
|
|
}
|
|
|
|
|
else if (nil == attributes)
|
|
|
|
|
{
|
|
|
|
|
attr = [NSMutableDictionary new];
|
|
|
|
|
attributes = attr;
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[this->tagPath addObject: tag];
|
|
|
|
|
[this->namespaces addObject: ((ns == nil) ? (id)null : (id)ns)];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->didStartElement != 0)
|
|
|
|
|
{
|
|
|
|
|
qualified = tag;
|
|
|
|
|
if (this->shouldProcessNamespaces)
|
|
|
|
|
{
|
|
|
|
|
NSRange r;
|
|
|
|
|
NSString *p = @"";
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
r = [tag rangeOfString: @":" options: NSLiteralSearch];
|
|
|
|
|
if (r.length > 0)
|
|
|
|
|
{
|
|
|
|
|
p = [tag substringToIndex: r.location];
|
|
|
|
|
tag = [tag substringFromIndex: NSMaxRange(r)];
|
|
|
|
|
}
|
|
|
|
|
uri = [self _uriForPrefix: p];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
uri = @"";
|
|
|
|
|
}
|
|
|
|
|
(*this->didStartElement)(_del,
|
|
|
|
|
didStartElementSel, self, tag, uri, qualified, attributes);
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
TEST_RELEASE(ns);
|
|
|
|
|
TEST_RELEASE(attr);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// closing tag
|
|
|
|
|
if (this->acceptHTML)
|
|
|
|
|
{
|
|
|
|
|
// lazily close any missing tags on stack
|
|
|
|
|
while ([this->tagPath count] > 0
|
|
|
|
|
&& ![[this->tagPath lastObject] isEqualToString: tag])
|
|
|
|
|
{
|
|
|
|
|
[self _closeLastTag];
|
|
|
|
|
}
|
|
|
|
|
if ([this->tagPath count] == 0)
|
|
|
|
|
{
|
|
|
|
|
return; // ignore closing tag without matching open...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (![[this->tagPath lastObject] isEqualToString: tag])
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: [NSString stringWithFormat:
|
|
|
|
|
@"tag nesting error (</%@> expected, </%@> found)",
|
|
|
|
|
[this->tagPath lastObject], tag]
|
|
|
|
|
code: NSXMLParserNotWellBalancedError];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
[self _closeLastTag];
|
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString*) _newEntity: (const unsigned char *)ep length: (int)len
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSString *entity;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (*ep == '#')
|
|
|
|
|
{
|
|
|
|
|
if (len < 8)
|
|
|
|
|
{
|
|
|
|
|
uint32_t val;
|
|
|
|
|
char buf[8];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
memcpy(buf, ep + 1, len - 1);
|
|
|
|
|
buf[len - 1] = '\0';
|
|
|
|
|
// &#ddd; or &#xhh;
|
|
|
|
|
if (sscanf(buf, "x%x;", &val) || sscanf(buf, "%d;", &val))
|
|
|
|
|
{
|
|
|
|
|
// &#xhh; hex value or &ddd; decimal value
|
|
|
|
|
if (val > 0xffff)
|
|
|
|
|
{
|
|
|
|
|
unichar buf[2];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* Convert codepoint outside base plane to surrogate pair
|
|
|
|
|
*/
|
|
|
|
|
val -= 0x010000;
|
|
|
|
|
buf[0] = (val / 0x400) + 0xd800;
|
|
|
|
|
buf[1] = (val % 0x400) + 0xdc00;
|
|
|
|
|
return [[NSString alloc] initWithCharacters: buf length: 2];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unichar c = (unichar)val;
|
|
|
|
|
|
|
|
|
|
return [[NSString alloc] initWithCharacters: &c length: 1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// the five predefined entities
|
|
|
|
|
if (len == 3 && strncmp((char *)ep, "amp", len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return @"&";
|
|
|
|
|
}
|
|
|
|
|
else if (len == 2 && strncmp((char *)ep, "lt", len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return @"<";
|
|
|
|
|
}
|
|
|
|
|
else if (len == 2 && strncmp((char *)ep, "gt", len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return @">";
|
|
|
|
|
}
|
|
|
|
|
else if (len == 4 && strncmp((char *)ep, "quot", len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return @"\"";
|
|
|
|
|
}
|
|
|
|
|
else if (len == 4 && strncmp((char *)ep, "apos", len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return @"'";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
entity = NewUTF8STR(ep, len);
|
|
|
|
|
if (nil == entity)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in entity name"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
2007-11-05 11:30:23 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (this->shouldResolveExternalEntities)
|
|
|
|
|
{
|
|
|
|
|
#if 1
|
|
|
|
|
NSLog(@"NSXMLParser: unrecognized entity: &%@;", entity);
|
|
|
|
|
#endif
|
|
|
|
|
// entity=[entitiesTable objectForKey: entity]; // look up string in entity translation table
|
2007-11-05 11:30:23 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == entity)
|
|
|
|
|
{
|
|
|
|
|
entity = @"&??;"; // unknown entity
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-06-19 14:28:48 +00:00
|
|
|
|
ASSIGN(entity, @""); // not resolved
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
return entity;
|
|
|
|
|
}
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) _parseEntity: (NSString**)result
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
int c;
|
|
|
|
|
NSUInteger ep = this->cp; // should be position behind &
|
|
|
|
|
int len;
|
|
|
|
|
NSString *entity;
|
2014-03-28 16:01:13 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (0 == result) result = &entity;
|
|
|
|
|
do {
|
|
|
|
|
c = cget();
|
|
|
|
|
} while (c != EOF && c != '<' && c != ';');
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (c != ';')
|
2009-02-22 20:01:53 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
// invalid sequence - end of file or missing ; before next tag
|
|
|
|
|
return NO;
|
2009-02-22 20:01:53 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
len = this->cp - ep - 1;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-08 14:05:17 +00:00
|
|
|
|
*result = [self _newEntity: addr(ep) length: len];
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (&entity == result)
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(*result); // Won't be used
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return YES;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString *) _newQarg
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
// get argument (might be quoted)
|
2020-05-08 14:05:17 +00:00
|
|
|
|
NSUInteger ap = --this->cp; // argument start pointer
|
|
|
|
|
int c = cget(); // refetch first character
|
|
|
|
|
int len;
|
|
|
|
|
BOOL containsEntity = NO;
|
|
|
|
|
NSString *qs;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"_newQarg: %02x %c", c, isprint(c)?c: ' ');
|
|
|
|
|
#endif
|
|
|
|
|
if (c == '\"')
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
do
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
c = cget();
|
|
|
|
|
if (c == EOF)
|
2010-05-21 17:40:43 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return nil; // unterminated!
|
2010-05-21 17:40:43 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ('&' == c)
|
|
|
|
|
{
|
|
|
|
|
containsEntity = YES;
|
|
|
|
|
}
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (c != '\"');
|
|
|
|
|
len = this->cp - ap - 2;
|
|
|
|
|
ap++;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
else if (c == '\'')
|
2009-02-22 20:01:53 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
do
|
2009-02-22 20:01:53 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
c = cget();
|
|
|
|
|
if (c == EOF)
|
|
|
|
|
{
|
|
|
|
|
return nil; // unterminated!
|
|
|
|
|
}
|
|
|
|
|
if ('&' == c)
|
|
|
|
|
{
|
|
|
|
|
containsEntity = YES;
|
|
|
|
|
}
|
2010-05-25 08:25:19 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (c != '\'');
|
|
|
|
|
len = this->cp - ap - 2;
|
|
|
|
|
ap++;
|
2009-02-22 20:01:53 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
else
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* strict XML requires quoting (?)
|
|
|
|
|
if (!this->acceptHTML)
|
|
|
|
|
;
|
|
|
|
|
*/
|
|
|
|
|
while (!isspace(c)
|
|
|
|
|
&& c != '>' && c != '/' && c != '?' && c != '=' && c != EOF)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ('&' == c)
|
|
|
|
|
{
|
|
|
|
|
containsEntity = YES;
|
|
|
|
|
}
|
|
|
|
|
c = cget();
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
this->cp--; // go back to terminating character
|
|
|
|
|
len = this->cp - ap;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (YES == containsEntity)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSString *seg;
|
|
|
|
|
NSMutableString *m;
|
2020-05-08 14:05:17 +00:00
|
|
|
|
const unsigned char *start = addr(ap);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
const unsigned char *end = start + len;
|
|
|
|
|
const unsigned char *ptr = start;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
m = [[NSMutableString alloc] initWithCapacity: len];
|
|
|
|
|
while (ptr < end)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (ptr < end && *ptr != '&')
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
ptr++;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (ptr > start)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
seg = NewUTF8STR(start, ptr - start);
|
|
|
|
|
if (nil == seg)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(m);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[self _parseError: @"invalid character in quoted string"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
[m appendString: seg];
|
|
|
|
|
RELEASE(seg);
|
|
|
|
|
start = ptr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (ptr < end && *ptr != ';')
|
|
|
|
|
{
|
|
|
|
|
ptr++;
|
|
|
|
|
}
|
|
|
|
|
seg = [self _newEntity: start + 1 length: ptr - start - 1];
|
|
|
|
|
[m appendString: seg];
|
|
|
|
|
RELEASE(seg);
|
|
|
|
|
if (ptr < end)
|
|
|
|
|
{
|
|
|
|
|
ptr++; // Step past trailing semicolon
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
start = ptr;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return m;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-08 14:05:17 +00:00
|
|
|
|
qs = NewUTF8STR(addr(ap), len);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == qs)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in quoted string"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
return qs;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) parse
|
2011-01-17 19:37:27 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
// read XML (or HTML) file
|
2020-05-08 14:05:17 +00:00
|
|
|
|
NSUInteger vp = this->cp; // value position
|
|
|
|
|
int c;
|
2011-01-17 19:37:27 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* Start by accumulating ignorable whitespace.
|
|
|
|
|
*/
|
|
|
|
|
this->ignorable = YES;
|
|
|
|
|
this->whitespace = YES;
|
|
|
|
|
c = cget(); // get first character
|
|
|
|
|
while (!this->abort)
|
2011-01-17 19:37:27 +00:00
|
|
|
|
{
|
|
|
|
|
#if EXTRA_DEBUG
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSLog(@"_nextelement %02x %c", c, isprint(c)?c: ' ');
|
2011-01-17 19:37:27 +00:00
|
|
|
|
#endif
|
2020-05-08 14:05:17 +00:00
|
|
|
|
switch (c)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
case '\r':
|
|
|
|
|
this->column = 0;
|
|
|
|
|
break;
|
2011-01-18 12:07:21 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
case '\n':
|
|
|
|
|
this->line++;
|
|
|
|
|
this->column = 0;
|
|
|
|
|
break;
|
2011-01-18 12:07:21 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
case '<':
|
|
|
|
|
/* Whitespace immediately before an element is always ignorable.
|
|
|
|
|
*/
|
|
|
|
|
this->ignorable = YES; /* Fall through to push out data */
|
|
|
|
|
case EOF:
|
|
|
|
|
case '&':
|
2014-03-28 16:01:13 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* push out any characters that have been collected so far
|
|
|
|
|
*/
|
|
|
|
|
if (this->cp - vp > 1)
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
NSUInteger p;
|
|
|
|
|
NSString *s;
|
2011-01-17 19:37:27 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
p = this->cp - 1;
|
|
|
|
|
if (YES == this->ignorable)
|
|
|
|
|
{
|
|
|
|
|
if (YES == this->whitespace)
|
|
|
|
|
{
|
|
|
|
|
p = vp; // all whitespace
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* step through trailing whitespace (if any)
|
|
|
|
|
*/
|
2020-05-08 14:05:17 +00:00
|
|
|
|
while (p > vp && isspace(addr(p)[-1]))
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
p--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (YES == this->hasElement)
|
|
|
|
|
{
|
|
|
|
|
if (p - vp > 0)
|
|
|
|
|
{
|
|
|
|
|
if (this->foundCharacters != 0)
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
s = NewUTF8STR(addr(vp), p - vp);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == s)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character data"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Process this data as characters
|
|
|
|
|
*/
|
|
|
|
|
(*this->foundCharacters)(_del,
|
|
|
|
|
foundCharactersSel, self, s);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(s);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (p < this->cp - 1)
|
|
|
|
|
{
|
|
|
|
|
if (this->foundIgnorable != 0)
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
s = NewUTF8STR(addr(p), this->cp - p - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == s)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid whitespace data"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Process data as ignorable whitespace
|
|
|
|
|
*/
|
|
|
|
|
(*this->foundIgnorable)(_del,
|
|
|
|
|
foundIgnorableSel, self, s);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(s);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (this->foundCharacters != 0)
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
s = NewUTF8STR(addr(p), this->cp - p - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == s)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character data"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Process data as characters
|
|
|
|
|
*/
|
|
|
|
|
(*this->foundCharacters)(_del,
|
|
|
|
|
foundCharactersSel, self, s);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(s);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
vp = this->cp;
|
|
|
|
|
}
|
2014-03-28 16:01:13 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2011-01-17 19:37:27 +00:00
|
|
|
|
|
2020-05-08 14:05:17 +00:00
|
|
|
|
switch (c)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
if (YES == this->whitespace && !isspace(c))
|
|
|
|
|
{
|
|
|
|
|
if (YES == this->ignorable && this->cp - vp > 1)
|
|
|
|
|
{
|
|
|
|
|
NSString *s;
|
2011-01-17 19:37:27 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* We have accumulated ignorable whitespace ...
|
|
|
|
|
* push it out.
|
|
|
|
|
*/
|
|
|
|
|
if (this->foundIgnorable != 0)
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
s = NewUTF8STR(addr(vp), this->cp - vp - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == s)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid whitespace data"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
(*this->foundIgnorable)(_del,
|
|
|
|
|
foundIgnorableSel, self, s);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(s);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (this->foundCharacters != 0)
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
s = NewUTF8STR(addr(vp), this->cp - vp - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (nil == s)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character data"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
(*this->foundCharacters)(_del,
|
|
|
|
|
foundCharactersSel, self, s);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(s);
|
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
vp = this->cp - 1;
|
|
|
|
|
}
|
|
|
|
|
/* We have read non-space data, so whitespace is no longer
|
|
|
|
|
* ignorable, and the buffer no longer contains only space.
|
|
|
|
|
*/
|
|
|
|
|
this->ignorable = NO;
|
|
|
|
|
this->whitespace = NO;
|
|
|
|
|
}
|
|
|
|
|
c = cget(); // just collect until we push out (again)
|
|
|
|
|
continue;
|
2011-01-18 12:07:21 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
case EOF:
|
|
|
|
|
{
|
|
|
|
|
if ([this->tagPath count] != 0)
|
|
|
|
|
{
|
|
|
|
|
if (!this->acceptHTML)
|
|
|
|
|
{
|
|
|
|
|
/* strict XML nesting error
|
|
|
|
|
*/
|
|
|
|
|
return [self _parseError: @"unexpected end of file"
|
|
|
|
|
code: NSXMLParserNotWellBalancedError];
|
|
|
|
|
}
|
|
|
|
|
while ([this->tagPath count] > 0)
|
|
|
|
|
{
|
|
|
|
|
// lazily close all open tags
|
|
|
|
|
if (this->didEndElement != 0)
|
|
|
|
|
{
|
|
|
|
|
(*this->didEndElement)(_del,
|
|
|
|
|
didEndElementSel, self,
|
|
|
|
|
[this->tagPath lastObject], nil, nil);
|
|
|
|
|
}
|
|
|
|
|
[this->tagPath removeLastObject]; // pop from stack
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-17 19:37:27 +00:00
|
|
|
|
#if EXTRA_DEBUG
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSLog(@"parserDidEndDocument: ");
|
2011-01-17 19:37:27 +00:00
|
|
|
|
#endif
|
2020-05-06 14:24:04 +00:00
|
|
|
|
|
|
|
|
|
if ([_del respondsToSelector: @selector(parserDidEndDocument:)])
|
|
|
|
|
{
|
|
|
|
|
[_del parserDidEndDocument: self];
|
|
|
|
|
}
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '&':
|
|
|
|
|
{
|
|
|
|
|
NSString *entity;
|
|
|
|
|
|
|
|
|
|
/* After any entity, whitespace is no longer ignorable, but
|
|
|
|
|
* we will have an empty buffer to accumulate it.
|
|
|
|
|
*/
|
|
|
|
|
this->ignorable = NO;
|
|
|
|
|
this->whitespace = YES;
|
|
|
|
|
|
|
|
|
|
if ([self _parseEntity: &entity] == NO)
|
|
|
|
|
{
|
|
|
|
|
return [self _parseError: @"empty entity name"
|
|
|
|
|
code: NSXMLParserEntityRefAtEOFError];
|
|
|
|
|
}
|
|
|
|
|
if (this->foundCharacters != 0)
|
2011-01-17 19:37:27 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
(*this->foundCharacters)(_del,
|
|
|
|
|
foundCharactersSel, self, entity);
|
|
|
|
|
}
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(entity);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
vp = this->cp; // next value sequence starts here
|
|
|
|
|
c = cget(); // first character behind ;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
|
{
|
|
|
|
|
NSString *tag;
|
|
|
|
|
NSMutableDictionary *attributes;
|
|
|
|
|
NSString *arg;
|
2020-05-08 14:05:17 +00:00
|
|
|
|
NSUInteger tp = this->cp; // tag position
|
|
|
|
|
NSUInteger sp = tp - 1; // Open angle bracket
|
2020-05-06 14:24:04 +00:00
|
|
|
|
|
|
|
|
|
/* After processing a tag, whitespace will be ignorable and
|
|
|
|
|
* we can start accumulating it in our buffer.
|
|
|
|
|
*/
|
|
|
|
|
this->ignorable = YES;
|
|
|
|
|
this->whitespace = YES;
|
|
|
|
|
|
|
|
|
|
if (this->cp < this->cend-3
|
2020-05-08 14:05:17 +00:00
|
|
|
|
&& strncmp((char *)addr(this->cp), "!--", 3) == 0)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
/* start of comment skip all characters until "-->"
|
|
|
|
|
*/
|
|
|
|
|
this->cp += 3;
|
|
|
|
|
tp = this->cp;
|
|
|
|
|
while (this->cp < this->cend-3
|
2020-05-08 19:59:28 +00:00
|
|
|
|
&& strncmp((char *)addr(this->cp), "-->", 3) != 0)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
this->cp++; // search
|
|
|
|
|
}
|
|
|
|
|
if (this->foundComment != 0)
|
2011-01-17 19:37:27 +00:00
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
NSString *c = NewUTF8STR(addr(tp), this->cp - tp);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
|
|
|
|
|
if (nil == c)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid comment data"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
(*this->foundComment)(_del,
|
|
|
|
|
foundCommentSel, self, c);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(c);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2011-01-17 19:37:27 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
this->cp += 3; // might go beyond cend ... ok
|
|
|
|
|
vp = this->cp; // value might continue
|
|
|
|
|
c = cget(); // get first character after comment
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (this->cp < this->cend-8
|
2020-05-08 14:05:17 +00:00
|
|
|
|
&& strncmp((char *)addr(this->cp), "![CDATA[", 8) == 0)
|
2011-01-17 19:37:27 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* start of CDATA skip all characters until "]>"
|
|
|
|
|
*/
|
|
|
|
|
this->cp += 8;
|
|
|
|
|
tp = this->cp;
|
|
|
|
|
while (this->cp < this->cend-3
|
2020-05-08 14:05:17 +00:00
|
|
|
|
&& strncmp((char *)addr(this->cp), "]]>", 3) != 0)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
|
|
|
|
this->cp++; // search
|
|
|
|
|
}
|
|
|
|
|
if (this->foundCDATA != 0)
|
|
|
|
|
{
|
|
|
|
|
NSData *d;
|
|
|
|
|
|
2020-05-08 14:05:17 +00:00
|
|
|
|
d = [[NSData alloc] initWithBytes: addr(tp)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
length: this->cp - tp];
|
|
|
|
|
(*this->foundCDATA)(_del,
|
|
|
|
|
foundCDATASel, self, d);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(d);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
this->cp += 3; // might go beyond cend ... ok
|
|
|
|
|
vp = this->cp; // value might continue
|
|
|
|
|
c = cget(); // get first character after CDATA
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = cget(); // get first character of tag
|
|
|
|
|
if (c == '/')
|
|
|
|
|
{
|
|
|
|
|
c = cget(); // closing tag </tag begins
|
|
|
|
|
}
|
|
|
|
|
else if (c == '?')
|
|
|
|
|
{
|
|
|
|
|
/* special tag <?tag begins
|
|
|
|
|
*/
|
|
|
|
|
c = cget(); // include in tag string
|
|
|
|
|
// NSLog(@"special tag <? found");
|
|
|
|
|
/* FIXME: this->should process this tag in a special
|
|
|
|
|
* way so that e.g. <?php any PHP script ?> is read
|
|
|
|
|
* as a single tag!
|
|
|
|
|
* to do this properly, we need a notion of comments
|
|
|
|
|
* and quoted string constants...
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
else if (c == '!')
|
|
|
|
|
{
|
|
|
|
|
/* declaration <!tag begins
|
|
|
|
|
*/
|
|
|
|
|
[self _processDeclaration];
|
|
|
|
|
vp = this->cp; // prepare for next value
|
|
|
|
|
c = cget(); // fetch next character
|
|
|
|
|
continue;
|
2011-01-17 19:37:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (c != EOF && !isspace(c)
|
|
|
|
|
&& c != '>' && c != '/' && c != '?')
|
|
|
|
|
{
|
|
|
|
|
c = cget(); // scan tag until we find a delimiting character
|
|
|
|
|
}
|
2020-05-08 14:05:17 +00:00
|
|
|
|
if (*addr(tp) == '/')
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
tag = NewUTF8STR(addr(tp + 1), this->cp - tp - 2);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
tag = NewUTF8STR(addr(tp), this->cp - tp - 1);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
|
|
|
|
if (nil == tag)
|
|
|
|
|
{
|
|
|
|
|
[self _parseError: @"invalid character in tag"
|
|
|
|
|
code: NSXMLParserInvalidCharacterError];
|
|
|
|
|
}
|
2007-11-05 11:30:23 +00:00
|
|
|
|
#if EXTRA_DEBUG
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSLog(@"tag=%@ - %02x %c", tag, c, isprint(c)?c: ' ');
|
2006-12-27 08:16:37 +00:00
|
|
|
|
#endif
|
2008-01-27 08:57:12 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* Create an attributes dictionary for this tag,
|
|
|
|
|
* using default values if available.
|
|
|
|
|
*/
|
|
|
|
|
attributes = [[this->defaults objectForKey: tag] mutableCopy];
|
|
|
|
|
if (nil == attributes)
|
|
|
|
|
{
|
|
|
|
|
attributes
|
|
|
|
|
= [[NSMutableDictionary alloc] initWithCapacity: 5];
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (isspace(c))
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
c = cget();
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
while (c != EOF)
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-08 14:05:17 +00:00
|
|
|
|
if (c == '/' && *addr(tp) != '/')
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
// appears to be a />
|
|
|
|
|
c = cget();
|
|
|
|
|
if (c != '>')
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(attributes);
|
|
|
|
|
RELEASE(tag);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [self _parseError: @"<tag/ is missing the >"
|
|
|
|
|
code: NSXMLParserGTRequiredError];
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[self _processTag: tag
|
|
|
|
|
isEnd: NO
|
|
|
|
|
withAttributes: attributes];
|
2022-12-09 10:23:14 +00:00
|
|
|
|
if (NO == this->abort)
|
|
|
|
|
{
|
|
|
|
|
[self _processTag: tag
|
|
|
|
|
isEnd: YES
|
|
|
|
|
withAttributes: nil];
|
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
break;
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
|
2020-05-08 14:05:17 +00:00
|
|
|
|
if (c == '?' && *addr(tp) == '?')
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
// appears to be a ?>
|
|
|
|
|
c = cget();
|
|
|
|
|
if (c != '>')
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(attributes);
|
|
|
|
|
RELEASE(tag);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [self _parseError:
|
|
|
|
|
@"<?tag ...? is missing the >"
|
|
|
|
|
code: NSXMLParserGTRequiredError];
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/* If this is the <?xml header, the opening angle
|
|
|
|
|
* bracket MUST be at the start of the data.
|
|
|
|
|
*/
|
2020-05-08 14:05:17 +00:00
|
|
|
|
if ([tag isEqualToString: @"?xml"] && sp != 0)
|
2020-05-06 14:24:04 +00:00
|
|
|
|
{
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE(attributes);
|
|
|
|
|
RELEASE(tag);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [self _parseError: @"bad <?xml > preamble"
|
|
|
|
|
code: NSXMLParserDocumentStartError];
|
|
|
|
|
}
|
|
|
|
|
[self _processTag: tag
|
|
|
|
|
isEnd: NO
|
|
|
|
|
withAttributes: attributes]; // single <?tag ...?>
|
|
|
|
|
break; // done
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
// this should also allow for line break and tab
|
|
|
|
|
while (isspace(c))
|
2008-01-27 08:57:12 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
c = cget();
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (c == '>')
|
|
|
|
|
{
|
2022-12-09 10:23:14 +00:00
|
|
|
|
if (NO == this->abort)
|
|
|
|
|
{
|
|
|
|
|
[self _processTag: tag
|
|
|
|
|
isEnd: (*addr(tp) == '/')
|
|
|
|
|
withAttributes: attributes];
|
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* get next argument (eats up to /, ?, >, =, space)
|
|
|
|
|
*/
|
|
|
|
|
arg = [self _newQarg];
|
|
|
|
|
#if EXTRA_DEBUG
|
|
|
|
|
NSLog(@"arg=%@", arg);
|
|
|
|
|
#endif
|
|
|
|
|
if (!this->acceptHTML && [arg length] == 0)
|
|
|
|
|
{
|
|
|
|
|
RELEASE(arg);
|
|
|
|
|
RELEASE(tag);
|
|
|
|
|
RELEASE(attributes);
|
|
|
|
|
return [self _parseError: @"empty attribute name"
|
|
|
|
|
code: NSXMLParserAttributeNotStartedError];
|
|
|
|
|
}
|
|
|
|
|
c = cget();
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
if (c == '=')
|
|
|
|
|
{
|
|
|
|
|
NSString *val;
|
|
|
|
|
|
|
|
|
|
// explicit assignment
|
|
|
|
|
c = cget(); // skip =
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
val = [self _newQarg];
|
|
|
|
|
[attributes setObject: val forKey: arg];
|
|
|
|
|
RELEASE(val);
|
|
|
|
|
c = cget(); // get character behind qarg value
|
|
|
|
|
while (isspace(c))
|
|
|
|
|
{
|
|
|
|
|
c = cget();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // implicit
|
|
|
|
|
{
|
|
|
|
|
[attributes setObject: @"" forKey: arg];
|
|
|
|
|
}
|
|
|
|
|
RELEASE(arg);
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
RELEASE(attributes);
|
|
|
|
|
RELEASE(tag);
|
|
|
|
|
vp = this->cp; // prepare for next value
|
|
|
|
|
c = cget(); // skip > and fetch next character
|
2008-01-27 08:57:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-12-27 08:16:37 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [self _parseError: @"aborted"
|
|
|
|
|
code: NSXMLParserDelegateAbortedParseError];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) acceptsHTML
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return this->acceptHTML;
|
|
|
|
|
}
|
2009-02-22 20:01:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) shouldProcessNamespaces
|
|
|
|
|
{
|
|
|
|
|
return this->shouldProcessNamespaces;
|
|
|
|
|
}
|
2012-10-18 21:44:50 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) shouldReportNamespacePrefixes
|
|
|
|
|
{
|
|
|
|
|
return this->shouldReportNamespacePrefixes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) shouldResolveExternalEntities
|
|
|
|
|
{
|
|
|
|
|
return this->shouldResolveExternalEntities;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setShouldProcessNamespaces: (BOOL)aFlag
|
|
|
|
|
{
|
|
|
|
|
this->shouldProcessNamespaces = aFlag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setShouldReportNamespacePrefixes: (BOOL)aFlag
|
|
|
|
|
{
|
|
|
|
|
this->shouldReportNamespacePrefixes = aFlag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setShouldResolveExternalEntities: (BOOL)aFlag
|
|
|
|
|
{
|
|
|
|
|
this->shouldResolveExternalEntities = aFlag;
|
|
|
|
|
}
|
2018-06-28 14:35:56 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) _setAcceptHTML: (BOOL) flag
|
|
|
|
|
{
|
|
|
|
|
this->acceptHTML = flag;
|
|
|
|
|
}
|
2018-06-28 14:35:56 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString *) publicID
|
|
|
|
|
{
|
|
|
|
|
return [self notImplemented: _cmd];
|
|
|
|
|
}
|
2009-02-22 20:01:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString *) systemID
|
|
|
|
|
{
|
|
|
|
|
return [self notImplemented: _cmd];
|
|
|
|
|
}
|
2009-02-22 20:01:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSObject (NSXMLParserDelegateEventAdditions)
|
|
|
|
|
- (NSData*) parser: (NSXMLParser*)aParser
|
|
|
|
|
resolveExternalEntityName: (NSString*)aName
|
|
|
|
|
systemID: (NSString*)aSystemID
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
2013-04-03 15:28:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
didEndElement: (NSString*)anElementName
|
|
|
|
|
namespaceURI: (NSString*)aNamespaceURI
|
|
|
|
|
qualifiedName: (NSString*)aQualifierName
|
2013-04-03 15:28:28 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2013-04-03 15:28:28 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
didEndMappingPrefix: (NSString*)aPrefix
|
|
|
|
|
{
|
|
|
|
|
}
|
2013-04-03 15:28:28 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
didStartElement: (NSString*)anElementName
|
|
|
|
|
namespaceURI: (NSString*)aNamespaceURI
|
|
|
|
|
qualifiedName: (NSString*)aQualifierName
|
|
|
|
|
attributes: (NSDictionary*)anAttributeDict
|
|
|
|
|
{
|
|
|
|
|
}
|
2013-04-03 15:28:28 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
didStartMappingPrefix: (NSString*)aPrefix
|
|
|
|
|
toURI: (NSString*)aNamespaceURI
|
|
|
|
|
{
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundAttributeDeclarationWithName: (NSString*)anAttributeName
|
|
|
|
|
forElement: (NSString*)anElementName
|
|
|
|
|
type: (NSString*)aType
|
|
|
|
|
defaultValue: (NSString*)aDefaultValue
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundCDATA: (NSData*)aBlock
|
|
|
|
|
{
|
|
|
|
|
}
|
2013-04-03 15:28:28 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundCharacters: (NSString*)aString
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundComment: (NSString*)aComment
|
|
|
|
|
{
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundElementDeclarationWithName: (NSString*)anElementName
|
|
|
|
|
model: (NSString*)aModel
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundExternalEntityDeclarationWithName: (NSString*)aName
|
|
|
|
|
publicID: (NSString*)aPublicID
|
|
|
|
|
systemID: (NSString*)aSystemID
|
|
|
|
|
{
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundIgnorableWhitespace: (NSString*)aWhitespaceString
|
|
|
|
|
{
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundInternalEntityDeclarationWithName: (NSString*)aName
|
|
|
|
|
value: (NSString*)aValue
|
|
|
|
|
{
|
|
|
|
|
}
|
2009-02-22 20:01:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundNotationDeclarationWithName: (NSString*)aName
|
|
|
|
|
publicID: (NSString*)aPublicID
|
|
|
|
|
systemID: (NSString*)aSystemID
|
|
|
|
|
{
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundProcessingInstructionWithTarget: (NSString*)aTarget
|
|
|
|
|
data: (NSString*)aData
|
|
|
|
|
{
|
|
|
|
|
}
|
2011-01-18 12:07:21 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
foundUnparsedEntityDeclarationWithName: (NSString*)aName
|
|
|
|
|
publicID: (NSString*)aPublicID
|
|
|
|
|
systemID: (NSString*)aSystemID
|
|
|
|
|
notationName: (NSString*)aNotationName
|
|
|
|
|
{
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
parseErrorOccurred: (NSError*)anError
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) parser: (NSXMLParser*)aParser
|
|
|
|
|
validationErrorOccurred: (NSError*)anError
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) parserDidEndDocument: (NSXMLParser*)aParser
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) parserDidStartDocument: (NSXMLParser*)aParser
|
|
|
|
|
{
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@end
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#if defined(HAVE_LIBXML)
|
2010-05-25 08:25:19 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#include <GNUstepBase/GSXML.h>
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@interface NSXMLSAXHandler : GSSAXHandler
|
|
|
|
|
{
|
|
|
|
|
@public
|
|
|
|
|
id _delegate; // Not retained
|
|
|
|
|
id _owner; // Not retained
|
|
|
|
|
NSError *_lastError;
|
|
|
|
|
BOOL _shouldProcessNamespaces;
|
|
|
|
|
BOOL _shouldReportNamespacePrefixes;
|
|
|
|
|
BOOL _shouldResolveExternalEntities;
|
|
|
|
|
NSMutableArray *_namespaces;
|
|
|
|
|
}
|
|
|
|
|
- (void) _setOwner: (id)owner;
|
|
|
|
|
@end
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
@implementation NSXMLSAXHandler
|
2010-05-25 08:25:19 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
}
|
2009-02-23 20:51:20 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
DESTROY(_namespaces);
|
|
|
|
|
DESTROY(_lastError);
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
2009-02-23 20:51:20 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) init
|
|
|
|
|
{
|
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
|
{
|
|
|
|
|
_namespaces = [NSMutableArray new];
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
2009-02-23 20:51:20 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) endDocument
|
|
|
|
|
{
|
|
|
|
|
[_delegate parserDidEndDocument: _owner];
|
|
|
|
|
}
|
|
|
|
|
- (void) startDocument
|
|
|
|
|
{
|
|
|
|
|
[_delegate parserDidStartDocument: _owner];
|
|
|
|
|
}
|
2009-02-22 20:01:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) startElement: (NSString*)elementName
|
|
|
|
|
prefix: (NSString*)prefix
|
|
|
|
|
href: (NSString*)href
|
|
|
|
|
attributes: (NSMutableDictionary*)elementAttributes
|
|
|
|
|
namespaces: (NSMutableDictionary*)elementNamespaces
|
|
|
|
|
{
|
|
|
|
|
NSString *qName = elementName;
|
2011-01-17 19:37:27 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([prefix length] > 0)
|
|
|
|
|
{
|
|
|
|
|
qName = [NSString stringWithFormat: @"%@:%@", prefix, qName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementAttributes == nil)
|
|
|
|
|
{
|
|
|
|
|
elementAttributes = [NSMutableDictionary dictionary];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([elementNamespaces count] > 0)
|
|
|
|
|
{
|
|
|
|
|
[_namespaces addObject: [elementNamespaces allKeys]];
|
|
|
|
|
if (_shouldReportNamespacePrefixes)
|
|
|
|
|
{
|
|
|
|
|
NSEnumerator *e = [elementNamespaces keyEnumerator];
|
|
|
|
|
NSString *k;
|
|
|
|
|
|
|
|
|
|
while ((k = [e nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
NSString *v = [elementNamespaces objectForKey: k];
|
|
|
|
|
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
didStartMappingPrefix: k
|
|
|
|
|
toURI: v];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[_namespaces addObject: null];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_shouldProcessNamespaces)
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
didStartElement: elementName
|
|
|
|
|
namespaceURI: (nil == href) ? @"" : href
|
|
|
|
|
qualifiedName: qName
|
|
|
|
|
attributes: elementAttributes];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* When we are not handling namespaces specially, any namespaces
|
|
|
|
|
* should appear as attributes of the element.
|
|
|
|
|
*/
|
|
|
|
|
if ([elementNamespaces count] > 0)
|
|
|
|
|
{
|
|
|
|
|
NSEnumerator *e = [elementNamespaces keyEnumerator];
|
|
|
|
|
NSString *k;
|
|
|
|
|
|
|
|
|
|
while ((k = [e nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
NSString *v = [elementNamespaces objectForKey: k];
|
2011-01-18 12:07:21 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([k length] == 0)
|
2008-02-06 17:30:50 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[elementAttributes setObject: v forKey: @"xmlns"];
|
2008-02-06 17:30:50 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
else
|
2008-01-26 08:34:58 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
k = [@"xmlns:" stringByAppendingString: k];
|
|
|
|
|
[elementAttributes setObject: v forKey: k];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
didStartElement: qName
|
|
|
|
|
namespaceURI: nil
|
|
|
|
|
qualifiedName: nil
|
|
|
|
|
attributes: elementAttributes];
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-01-26 08:34:58 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) endElement: (NSString*)elementName
|
|
|
|
|
prefix: (NSString*)prefix
|
|
|
|
|
href: (NSString*)href
|
|
|
|
|
{
|
|
|
|
|
NSString *qName = elementName;
|
2009-02-22 20:01:53 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if ([prefix length] > 0)
|
|
|
|
|
{
|
|
|
|
|
qName = [NSString stringWithFormat: @"%@:%@", prefix, qName];
|
|
|
|
|
}
|
|
|
|
|
if (_shouldProcessNamespaces)
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
didEndElement: elementName
|
|
|
|
|
namespaceURI: (nil == href) ? @"" : href
|
|
|
|
|
qualifiedName: qName];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
didEndElement: qName
|
|
|
|
|
namespaceURI: nil
|
|
|
|
|
qualifiedName: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_shouldReportNamespacePrefixes)
|
|
|
|
|
{
|
|
|
|
|
id o = [_namespaces lastObject];
|
|
|
|
|
|
|
|
|
|
if (o != (id)null)
|
|
|
|
|
{
|
|
|
|
|
NSEnumerator *e = [(NSArray*)o objectEnumerator];
|
|
|
|
|
NSString *k;
|
|
|
|
|
|
|
|
|
|
while ((k = [e nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner didEndMappingPrefix: k];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[_namespaces removeLastObject];
|
|
|
|
|
}
|
|
|
|
|
- (void) attribute: (NSString*) name value: (NSString*)value
|
|
|
|
|
{
|
|
|
|
|
// FIXME
|
|
|
|
|
}
|
|
|
|
|
- (void) characters: (NSString*)string
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundCharacters: string];
|
|
|
|
|
}
|
|
|
|
|
- (void) ignoreWhitespace: (NSString*) ch
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundIgnorableWhitespace: ch];
|
|
|
|
|
}
|
|
|
|
|
- (void) processInstruction: (NSString*)targetName data: (NSString*)PIdata
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundProcessingInstructionWithTarget: targetName
|
|
|
|
|
data: PIdata];
|
|
|
|
|
}
|
|
|
|
|
- (void) comment: (NSString*) value
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundComment: value];
|
|
|
|
|
}
|
|
|
|
|
- (void) cdataBlock: (NSData*)value
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundCDATA: value];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Called to return the filename from which an entity should be loaded.
|
|
|
|
|
*/
|
|
|
|
|
- (NSString*) loadEntity: (NSString*)publicId
|
|
|
|
|
at: (NSString*)location
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An old global namespace has been parsed.
|
|
|
|
|
*/
|
|
|
|
|
- (void) namespaceDecl: (NSString*)name
|
|
|
|
|
href: (NSString*)href
|
|
|
|
|
prefix: (NSString*)prefix
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) notationDecl: (NSString*)name
|
|
|
|
|
public: (NSString*)publicId
|
|
|
|
|
system: (NSString*)systemId
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundNotationDeclarationWithName: name
|
|
|
|
|
publicID: publicId
|
|
|
|
|
systemID: systemId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An entity definition has been parsed.
|
|
|
|
|
*/
|
|
|
|
|
- (void) entityDecl: (NSString*)name
|
|
|
|
|
type: (NSInteger)type
|
|
|
|
|
public: (NSString*)publicId
|
|
|
|
|
system: (NSString*)systemId
|
|
|
|
|
content: (NSString*)content
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) attributeDecl: (NSString*)nameElement
|
|
|
|
|
name: (NSString*)name
|
|
|
|
|
type: (NSInteger)type
|
|
|
|
|
typeDefValue: (NSInteger)defType
|
|
|
|
|
defaultValue: (NSString*)value
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundAttributeDeclarationWithName: name
|
|
|
|
|
forElement: nameElement
|
|
|
|
|
type: @"" // FIXME
|
|
|
|
|
defaultValue: value];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) elementDecl: (NSString*)name
|
|
|
|
|
type: (NSInteger)type
|
|
|
|
|
{
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
foundElementDeclarationWithName: name
|
|
|
|
|
model: @""]; // FIXME
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* What to do when an unparsed entity declaration is parsed.
|
|
|
|
|
*/
|
|
|
|
|
- (void) unparsedEntityDecl: (NSString*)name
|
|
|
|
|
public: (NSString*)publicId
|
|
|
|
|
system: (NSString*)systemId
|
|
|
|
|
notationName: (NSString*)notation
|
|
|
|
|
{
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/**
|
|
|
|
|
* Called when an entity reference is detected.
|
|
|
|
|
*/
|
|
|
|
|
- (void) reference: (NSString*) name
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/**
|
|
|
|
|
* An old global namespace has been parsed.
|
|
|
|
|
*/
|
|
|
|
|
- (void) globalNamespace: (NSString*)name
|
|
|
|
|
href: (NSString*)href
|
|
|
|
|
prefix: (NSString*)prefix
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
/**
|
|
|
|
|
* Called when a warning message needs to be output.
|
|
|
|
|
*/
|
|
|
|
|
- (void) warning: (NSString*)e
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
GSPrintf(stderr, @"%@", e);
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) error: (NSString*)e
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSError *error;
|
|
|
|
|
NSDictionary *d;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
|
e, NSLocalizedDescriptionKey,
|
|
|
|
|
nil];
|
|
|
|
|
error = [NSError errorWithDomain: NSXMLParserErrorDomain
|
|
|
|
|
code: 0
|
|
|
|
|
userInfo: d];
|
|
|
|
|
ASSIGN(_lastError, error);
|
|
|
|
|
[_delegate parser: _owner
|
|
|
|
|
parseErrorOccurred: error];
|
|
|
|
|
}
|
|
|
|
|
- (void) fatalError: (NSString*)e
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
[self error: e];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) warning: (NSString*)e
|
|
|
|
|
colNumber: (NSInteger)colNumber
|
|
|
|
|
lineNumber: (NSInteger)lineNumber
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
e = [NSString stringWithFormat: @"at line: %d column: %d ... %@",
|
|
|
|
|
(int)lineNumber, (int)colNumber, e];
|
|
|
|
|
[self warning: e];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) error: (NSString*)e
|
|
|
|
|
colNumber: (NSInteger)colNumber
|
|
|
|
|
lineNumber: (NSInteger)lineNumber
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
e = [NSString stringWithFormat: @"at line: %d column: %d ... %@",
|
|
|
|
|
(int)lineNumber, (int)colNumber, e];
|
|
|
|
|
[self error: e];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) fatalError: (NSString*)e
|
|
|
|
|
colNumber: (NSInteger)colNumber
|
|
|
|
|
lineNumber: (NSInteger)lineNumber
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
e = [NSString stringWithFormat: @"at line: %d column: %d ... %@",
|
|
|
|
|
(int)lineNumber, (int)colNumber, e];
|
|
|
|
|
[self fatalError: e];
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSInteger) hasInternalSubset
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
- (BOOL) internalSubset: (NSString*)name
|
|
|
|
|
externalID: (NSString*)externalID
|
|
|
|
|
systemID: (NSString*)systemID
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
- (NSInteger) hasExternalSubset
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
- (BOOL) externalSubset: (NSString*)name
|
|
|
|
|
externalID: (NSString*)externalID
|
|
|
|
|
systemID: (NSString*)systemID
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
- (void*) getEntity: (NSString*)name
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
- (void*) getParameterEntity: (NSString*)name
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) _setOwner: (id)owner
|
2006-12-26 07:00:41 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
_owner = owner;
|
2006-12-26 07:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation GSStrictXMLParser
|
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
if (null == nil)
|
|
|
|
|
{
|
|
|
|
|
null = RETAIN([NSNull null]);
|
2020-05-08 14:26:07 +00:00
|
|
|
|
RELEASE([NSObject leakAt: &null]);
|
2020-05-06 14:24:04 +00:00
|
|
|
|
}
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#define myParser ((GSXMLParser*)_parser)
|
|
|
|
|
#define myHandler ((NSXMLSAXHandler*)_handler)
|
|
|
|
|
|
|
|
|
|
- (void) abortParsing
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
NSDictionary *d;
|
|
|
|
|
NSString *e;
|
|
|
|
|
NSError *error;
|
|
|
|
|
|
|
|
|
|
e = @"Parsing aborted";
|
|
|
|
|
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
|
e, NSLocalizedDescriptionKey,
|
|
|
|
|
nil];
|
|
|
|
|
error = [NSError errorWithDomain: NSXMLParserErrorDomain
|
|
|
|
|
code: 0
|
|
|
|
|
userInfo: d];
|
|
|
|
|
ASSIGN(myHandler->_lastError, error);
|
|
|
|
|
[myHandler->_delegate parser: myHandler->_owner parseErrorOccurred: error];
|
|
|
|
|
[myParser abortParsing];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) dealloc
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
DESTROY(_parser);
|
|
|
|
|
DESTROY(_handler);
|
|
|
|
|
[super dealloc];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) delegate
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return myHandler->_delegate;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) initWithContentsOfURL: (NSURL*)anURL
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
_handler = [NSXMLSAXHandler new];
|
|
|
|
|
[myHandler _setOwner: self];
|
|
|
|
|
_parser = [[GSXMLParser alloc] initWithSAXHandler: myHandler
|
|
|
|
|
withContentsOfURL: anURL];
|
|
|
|
|
[(GSXMLParser*)_parser substituteEntities: YES];
|
|
|
|
|
return self;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) initWithData: (NSData*)data
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
_handler = [NSXMLSAXHandler new];
|
|
|
|
|
[myHandler _setOwner: self];
|
|
|
|
|
_parser = [[GSXMLParser alloc] initWithSAXHandler: myHandler
|
|
|
|
|
withData: data];
|
|
|
|
|
[(GSXMLParser*)_parser substituteEntities: YES];
|
|
|
|
|
return self;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (id) initWithStream: (NSInputStream*)stream
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
_handler = [NSXMLSAXHandler new];
|
|
|
|
|
[myHandler _setOwner: self];
|
|
|
|
|
_parser = [[GSXMLParser alloc] initWithSAXHandler: myHandler
|
|
|
|
|
withInputStream: stream];
|
|
|
|
|
[(GSXMLParser*)_parser substituteEntities: YES];
|
|
|
|
|
return self;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) parse
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
BOOL result;
|
|
|
|
|
|
|
|
|
|
result = [[myHandler parser] parse];
|
|
|
|
|
return result;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSError*) parserError
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return (nil == myHandler) ? nil : myHandler->_lastError;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) setDelegate: (id)delegate
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
myHandler->_delegate = delegate;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) setShouldProcessNamespaces: (BOOL)aFlag
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
myHandler->_shouldProcessNamespaces = aFlag;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) setShouldReportNamespacePrefixes: (BOOL)aFlag
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
myHandler->_shouldReportNamespacePrefixes = aFlag;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (void) setShouldResolveExternalEntities: (BOOL)aFlag
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
myHandler->_shouldResolveExternalEntities = aFlag;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) shouldProcessNamespaces
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return myHandler->_shouldProcessNamespaces;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) shouldReportNamespacePrefixes
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return myHandler->_shouldReportNamespacePrefixes;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (BOOL) shouldResolveExternalEntities
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return myHandler->_shouldResolveExternalEntities;
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSInteger) columnNumber
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [myParser columnNumber];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSInteger) lineNumber
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [myParser lineNumber];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString*) publicID
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [myParser publicID];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
- (NSString*) systemID
|
2004-10-20 10:48:04 +00:00
|
|
|
|
{
|
2020-05-06 14:24:04 +00:00
|
|
|
|
return [myParser systemID];
|
2004-10-20 10:48:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2020-05-06 14:24:04 +00:00
|
|
|
|
#else
|
|
|
|
|
@implementation GSStrictXMLParser
|
|
|
|
|
@end
|
|
|
|
|
#endif
|