diff --git a/ChangeLog b/ChangeLog index 89160331d..1ffc9a5fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-11-05 Richard Frith-Macdonald + + * Source/NSXML.m: Make sloppy parser available for internal use when + parsing bad xml produced by Apple tools. + * Source/NSPropertyList.m: Use GSSloppyXMLParser if normal xml2 based + parsing fails due to Ap[ple4's inclusioon of illegal characters in + the XML. + 2007-10-31 Richard Frith-Macdonald * Source/NSUserDefaults.m: ([setBoolForKey:]) changed to store YES diff --git a/Source/NSPropertyList.m b/Source/NSPropertyList.m index da03d65f6..2fa467cea 100644 --- a/Source/NSPropertyList.m +++ b/Source/NSPropertyList.m @@ -44,11 +44,14 @@ #include "Foundation/NSUserDefaults.h" #include "Foundation/NSValue.h" #include "Foundation/NSDebug.h" +#include "Foundation/NSNull.h" #include "Foundation/NSXMLParser.h" #include "GNUstepBase/Unicode.h" #include "GSPrivate.h" +@class GSSloppyXMLParser; + extern BOOL GSScanDouble(unichar*, unsigned, double*); @class GSMutableDictionary; @@ -103,7 +106,7 @@ extern BOOL GSScanDouble(unichar*, unsigned, double*); if ((self = [super init]) != nil) { stack = [[NSMutableArray alloc] initWithCapacity: 10]; - theParser = [[NSXMLParser alloc] initWithData: data]; + theParser = [[GSSloppyXMLParser alloc] initWithData: data]; [theParser setDelegate: self]; opts = options; } @@ -113,11 +116,15 @@ extern BOOL GSScanDouble(unichar*, unsigned, double*); - (void) parser: (NSXMLParser *)parser foundCharacters: (NSString *)string { - if (value == nil) + string = [string stringByTrimmingSpaces]; + if ([string length] > 0) { - value = [[NSMutableString alloc] initWithCapacity: 50]; + if (value == nil) + { + value = [[NSMutableString alloc] initWithCapacity: 50]; + } + [value appendString: string]; } - [value appendString: string]; } - (void) parser: (NSXMLParser *)parser @@ -130,6 +137,12 @@ extern BOOL GSScanDouble(unichar*, unsigned, double*); { NSMutableDictionary *d; + if (key == nil) + { + key = RETAIN([NSNull null]); + } + [stack addObject: key]; + DESTROY(key); d = [[NSMutableDictionary alloc] initWithCapacity: 10]; [stack addObject: d]; RELEASE(d); @@ -140,6 +153,12 @@ extern BOOL GSScanDouble(unichar*, unsigned, double*); { NSMutableArray *a; + if (key == nil) + { + key = RETAIN([NSNull null]); + } + [stack addObject: key]; + DESTROY(key); a = [[NSMutableArray alloc] initWithCapacity: 10]; [stack addObject: a]; RELEASE(a); @@ -174,13 +193,19 @@ extern BOOL GSScanDouble(unichar*, unsigned, double*); { ASSIGN(plist, [[stack lastObject] makeImmutableCopyOnFail: NO]); } + [stack removeLastObject]; inArray = NO; inDictionary = NO; + ASSIGN(key, [stack lastObject]); + [stack removeLastObject]; + if ((id)key == (id)[NSNull null]) + { + DESTROY(key); + } if ([stack count] > 0) { id last; - [stack removeLastObject]; last = [stack lastObject]; if ([last isKindOfClass: [NSArray class]] == YES) { @@ -2584,6 +2609,7 @@ GSPropertyListMake(id obj, NSDictionary *loc, BOOL xml, parser = [GSXMLParser parser]; [parser substituteEntities: YES]; [parser doValidityChecking: YES]; + [parser saveMessages: YES]; if ([parser parse: data] == NO || [parser parse: nil] == NO) { error = @"failed to parse as valid XML matching DTD"; diff --git a/Source/NSXMLParser.m b/Source/NSXMLParser.m index 1b4a339d7..7b1304add 100644 --- a/Source/NSXMLParser.m +++ b/Source/NSXMLParser.m @@ -33,7 +33,15 @@ NSString* const NSXMLParserErrorDomain = @"NSXMLParserErrorDomain"; -#ifdef HAVE_LIBXML +#if defined(HAVE_LIBXML) + +/* We support a strict libxml2 based parser ... but sometimes we need a + * sloppier parser which copes with bad XML generated by Apple's software, + * so we call the sloppy parser GSSloppyXMLParser. + */ +#define SloppyXMLParser GSSloppyXMLParser +@interface GSSloppyXMLParser : NSXMLParser +@end #include @@ -442,6 +450,18 @@ NSString* const NSXMLParserErrorDomain = @"NSXMLParserErrorDomain"; #else +/* We have no strict libxml based parser, so we use the sloppy parser + * as the normal NSXMLParser and create a GSSloppyXMLParser subclass + * so that we can still specify GSSloppyXMLParser in other files. + */ +#define SloppyXMLParser NSXMLParser +@interface GSSloppyXMLParser : NSXMLParser +@end +@implementation GSSloppyXMLParser +@end +#endif + + @implementation NSString (NSXMLParser) - (NSString *) _stringByExpandingXMLEntities @@ -485,8 +505,13 @@ typedef struct NSXMLParserIvarsType BOOL acceptHTML; // be lazy with bad tag nesting } NSXMLParserIvars; -@implementation NSXMLParser +@implementation SloppyXMLParser +#define EXTRA_DEBUG 0 + +typedef struct { @defs(NSXMLParser) } *xp; +#define _parser (((xp)self)->_parser) +#define _handler (((xp)self)->_handler) #define this ((NSXMLParserIvars*)_parser) #define _del ((id)_handler) @@ -508,6 +533,8 @@ typedef struct NSXMLParserIvarsType RELEASE(this->error); RELEASE(this->tagPath); NSZoneFree([self zone], this); + _parser = 0; + _handler = 0; } [super dealloc]; } @@ -568,7 +595,7 @@ typedef struct NSXMLParserIvarsType - (BOOL) _parseError: (NSString *)message { -#if 0 +#if EXTRA_DEBUG NSLog(@"XML parseError: %@", message); #endif NSError *err = nil; @@ -590,7 +617,7 @@ typedef struct NSXMLParserIvarsType { if ([tag isEqualToString: @"?xml"]) { -#if 0 +#if EXTRA_DEBUG NSLog(@"parserDidStartDocument: "); #endif if ([_del respondsToSelector: @selector(parserDidStartDocument:)]) @@ -599,7 +626,7 @@ NSLog(@"parserDidStartDocument: "); } if ([tag hasPrefix: @"?"]) { -#if 0 +#if EXTRA_DEBUG NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); #endif // parser: foundProcessingInstructionWithTarget: data: @@ -607,14 +634,14 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); } if ([tag isEqualToString: @"!DOCTYPE"]) { -#if 0 +#if EXTRA_DEBUG NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); #endif return; } if ([tag isEqualToString: @"!ENTITY"]) { -#if 0 +#if EXTRA_DEBUG NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); #endif return; @@ -623,7 +650,7 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); { // pass through as NSData // parser: foundCDATA: -#if 0 +#if EXTRA_DEBUG NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); #endif return; @@ -729,7 +756,7 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); const unsigned char *ap = --this->cp; // argument start pointer int c = cget(); // refetch first character -#if 0 +#if EXTRA_DEBUG NSLog(@"_qarg: %02x %c", c, isprint(c)?c: ' '); #endif if (c == '\"') @@ -777,7 +804,7 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); while (!this->abort) { // parse next element -#if 0 +#if EXTRA_DEBUG NSLog(@"_nextelement %02x %c", c, isprint(c)?c: ' '); #endif switch(c) @@ -821,7 +848,7 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); [this->tagPath removeLastObject]; // pop from stack } } -#if 0 +#if EXTRA_DEBUG NSLog(@"parserDidEndDocument: "); #endif @@ -878,7 +905,7 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); tag = UTF8STR(tp + 1, this->cp - tp - 2); // don't include / and delimiting character else tag = UTF8STR(tp, this->cp - tp - 1); // don't include delimiting character -#if 0 +#if EXTRA_DEBUG NSLog(@"tag=%@ - %02x %c", tag, c, isprint(c)?c: ' '); #endif parameters=[NSMutableDictionary dictionaryWithCapacity: 5]; @@ -913,7 +940,7 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); break; } arg=[self _qarg]; // get next argument (eats up to /, ?, >, =, space) -#if 0 +#if EXTRA_DEBUG NSLog(@"arg=%@", arg); #endif if (!this->acceptHTML && [arg length] == 0) @@ -989,8 +1016,6 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, attributes); @end -#endif - @implementation NSObject (NSXMLParserDelegateEventAdditions) - (NSData*) parser: (NSXMLParser*)aParser resolveExternalEntityName: (NSString*)aName