1/*
2  Copyright (C) 2000-2005 SKYRIX Software AG
3
4  This file is part of SOPE.
5
6  SOPE is free software; you can redistribute it and/or modify it under
7  the terms of the GNU Lesser General Public License as published by the
8  Free Software Foundation; either version 2, or (at your option) any
9  later version.
10
11  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12  WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14  License for more details.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with SOPE; see the file COPYING.  If not, write to the
18  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19  02111-1307, USA.
20*/
21
22#import "libxmlHTMLSAXDriver.h"
23#import "libxmlSAXLocator.h"
24#include "TableCallbacks.h"
25#include <SaxObjC/SaxObjC.h>
26#include <SaxObjC/SaxException.h>
27#include "common.h"
28#include <string.h>
29
30#include <libxml/HTMLparser.h>
31#include <libxml/HTMLtree.h>
32
33@interface NSObject(contentHandlerExtensions)
34
35- (xmlCharEncoding)contentEncoding;
36
37@end
38
39@interface libxmlHTMLSAXDriver(PrivateMethods)
40
41- (void)tearDownParser;
42
43- (BOOL)walkDocumentTree:(xmlDocPtr)_doc;
44- (BOOL)processNode:(xmlNodePtr)_node;
45- (BOOL)processChildren:(xmlNodePtr)children;
46
47@end
48
49static int _UTF8ToUTF16(unsigned char **sourceStart, unsigned char *sourceEnd,
50                        unichar **targetStart, const unichar *targetEnd);
51
52static BOOL       logUnsupportedFeatures = NO;
53static BOOL       reportInvalidTags      = NO;
54static BOOL       reportUnclosedEntities = NO;
55static NSMapTable *uniqueStrings = NULL; // THREAD
56static Class      NSStringClass = Nil;
57
58/* error string detection */
59/*
60  TODO: obviously this may change between libxml versions or even
61        localisations ... why doesn't libxml support error codes ?
62        (or does it ?)
63*/
64static const char *tagInvalidMsg = "tag %s invalid";
65static const char *unclosedEntityInvalidMsg =
66  "htmlParseEntityRef: expecting ';'";
67#if 0
68static const char *unexpectedNobrCloseMsg =
69  "Unexpected end tag : %s";
70#endif
71
72static inline NSString *xmlCharsToString(const xmlChar *_s) {
73  NSString *s;
74  char *newkey;
75
76  if (_s == NULL) return nil;
77
78  if (uniqueStrings == NULL) {
79    uniqueStrings = NSCreateMapTable(libxmlNonOwnedCStringMapKeyCallBacks,
80                                     NSObjectMapValueCallBacks,
81                                     128);
82  }
83  else if ((s = NSMapGet(uniqueStrings, _s))) {
84    /* found a string in cache ... */
85    return [s retain];
86  }
87
88  newkey = malloc(strlen((char *)_s) + 2);
89  strcpy(newkey, (char *)_s);
90
91  if (NSStringClass == Nil)
92    NSStringClass = [NSString class];
93
94  s = [[NSStringClass alloc] initWithUTF8String:(const char *)_s];
95  NSMapInsert(uniqueStrings, newkey, s);
96  return s;
97}
98
99static NSString *SaxDeclHandlerProperty =
100  @"http://xml.org/sax/properties/declaration-handler";
101static NSString *SaxLexicalHandlerProperty =
102  @"http://xml.org/sax/properties/lexical-handler";
103
104static NSString *XMLNS_XHTML = @"http://www.w3.org/1999/xhtml";
105
106@implementation libxmlHTMLSAXDriver
107
108static libxmlHTMLSAXDriver *activeDriver = nil;
109static void warning(void *udata, const char *msg, ...);
110static void error(void *udata, const char *msg, ...);
111static void fatalError(void *udata, const char *msg, ...);
112static void setLocator(void *udata, xmlSAXLocatorPtr _locator);
113
114+ (void)initialize {
115  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
116
117  reportInvalidTags  = [ud boolForKey:@"libxmlHTMLSAXDriverReportInvalidTags"];
118  reportUnclosedEntities =
119    [ud boolForKey:@"libxmlHTMLSAXDriverReportUnclosedEntityRefs"];
120}
121
122- (id)init {
123  if ((self = [super init])) {
124    self->namespaceURI   = [XMLNS_XHTML copy];
125    self->encodeEntities = NO;
126  }
127  return self;
128}
129
130- (void)dealloc {
131  [self tearDownParser];
132
133  [self->attributes     release];
134  [self->namespaceURI   release];
135  [self->lexicalHandler release];
136  [self->declHandler    release];
137  [self->contentHandler release];
138  [self->dtdHandler     release];
139  [self->errorHandler   release];
140  [self->entityResolver release];
141  [super dealloc];
142}
143
144/* features & properties */
145
146- (void)setFeature:(NSString *)_name to:(BOOL)_value {
147  if (logUnsupportedFeatures)
148    NSLog(@"%s: don't know feature %@", __PRETTY_FUNCTION__, _name);
149}
150- (BOOL)feature:(NSString *)_name {
151  if (logUnsupportedFeatures)
152    NSLog(@"%s: don't know feature %@", __PRETTY_FUNCTION__, _name);
153  return NO;
154}
155
156- (void)setProperty:(NSString *)_name to:(id)_value {
157  if ([_name isEqualToString:SaxLexicalHandlerProperty]) {
158    ASSIGN(self->lexicalHandler, _value);
159    return;
160  }
161  if ([_name isEqualToString:SaxDeclHandlerProperty]) {
162    ASSIGN(self->declHandler, _value);
163    return;
164  }
165
166  [SaxNotRecognizedException raise:@"PropertyException"
167                             format:@"don't know property %@", _name];
168}
169- (id)property:(NSString *)_name {
170  if ([_name isEqualToString:SaxLexicalHandlerProperty])
171    return self->lexicalHandler;
172  if ([_name isEqualToString:SaxDeclHandlerProperty])
173    return self->declHandler;
174
175  [SaxNotRecognizedException raise:@"PropertyException"
176                             format:@"don't know property %@", _name];
177  return nil;
178}
179
180/* handlers */
181
182- (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
183  ASSIGN(self->dtdHandler, _handler);
184}
185- (id<NSObject,SaxDTDHandler>)dtdHandler {
186  return self->dtdHandler;
187}
188
189- (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
190  ASSIGN(self->errorHandler, _handler);
191}
192- (id<NSObject,SaxErrorHandler>)errorHandler {
193  return self->errorHandler;
194}
195
196- (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
197  ASSIGN(self->entityResolver, _handler);
198}
199- (id<NSObject,SaxEntityResolver>)entityResolver {
200  return self->entityResolver;
201}
202
203- (void)setContentHandler:(id <NSObject,SaxContentHandler>)_handler {
204  ASSIGN(self->contentHandler, _handler);
205}
206- (id <NSObject,SaxContentHandler>)contentHandler {
207  return self->contentHandler;
208}
209
210/* libxml */
211
212- (void)setupParserWithDocumentPath:(NSString *)_path {
213  xmlSAXHandler   sax;
214  xmlCharEncoding charEncoding;
215
216  if (self->ctxt != NULL) {
217    NSLog(@"WARNING(%s): HTML parser context already setup !",
218          __PRETTY_FUNCTION__);
219    [self tearDownParser];
220  }
221
222  memcpy(&sax, &htmlDefaultSAXHandler, sizeof(xmlSAXHandler));
223  sax.error              = error;
224  sax.warning            = warning;
225  sax.fatalError         = fatalError;
226  sax.setDocumentLocator = setLocator;
227
228  if (activeDriver != nil) {
229    NSLog(@"WARNING(%s): %@ there is an active driver set (%@), override !",
230          __PRETTY_FUNCTION__, self, activeDriver);
231  }
232  activeDriver = self;
233
234  // hh: thats really a very ugly hack. The content-handler is for handling
235  //     content, not for dealing with the input data.
236  // TBD: the charset should be derived from the input (and this method should
237  //      probably take a charset)
238  if ([self->contentHandler respondsToSelector:@selector(contentEncoding)])
239    charEncoding = [self->contentHandler contentEncoding];
240  else
241    charEncoding = XML_CHAR_ENCODING_8859_1;
242
243  // TBD: do not use cString (nor UTF8String) but NSFileManager to convert
244  //      a string into a path
245  self->ctxt = htmlCreatePushParserCtxt(&sax             /* sax      */,
246                                        NULL /*self*/    /* userdata */,
247                                        NULL             /* chunk    */,
248                                        0                /* chunklen */,
249                                        [_path cString]  /* filename */,
250                                        charEncoding     /* encoding */);
251  self->doc = NULL;
252}
253- (void)tearDownParser {
254  if (activeDriver == self)
255    activeDriver = nil;
256
257  if (self->doc) {
258    xmlFreeDoc(self->doc);
259    self->doc = NULL;
260  }
261  if (self->ctxt) {
262    htmlFreeParserCtxt(self->ctxt);
263    self->ctxt = NULL;
264  }
265}
266
267/* IO */
268
269- (void)pushBytes:(const char *)_bytes count:(unsigned)_len {
270  if (_len == 0) return;
271  NSAssert(self->ctxt, @"missing HTML parser context");
272  htmlParseChunk(self->ctxt, _bytes, _len, 0);
273}
274- (void)pushEOF {
275  char dummyByte;
276  htmlParseChunk(self->ctxt, &dummyByte, 0, 1 /* terminate */);
277  self->doc = ((xmlParserCtxtPtr)ctxt)->myDoc;
278}
279
280/* parsing */
281
282- (void)_handleEmptyDataInSystemId:(NSString *)_sysId {
283  /*
284     An empty HTML file _is_ valid?!
285     I guess it equals to <html><body></body></html>, wrong? => hh
286  */
287  [self->contentHandler startDocument];
288  [self->contentHandler startPrefixMapping:@"" uri:self->namespaceURI];
289
290  [self->contentHandler
291       startElement:@"html" namespace:XMLNS_XHTML
292       rawName:@"html" attributes:nil];
293  [self->contentHandler
294       startElement:@"body" namespace:XMLNS_XHTML
295       rawName:@"body" attributes:nil];
296
297  [self->contentHandler
298       endElement:@"body" namespace:XMLNS_XHTML rawName:@"body"];
299  [self->contentHandler
300       endElement:@"html" namespace:XMLNS_XHTML rawName:@"html"];
301
302  [self->contentHandler endPrefixMapping:@""];
303  [self->contentHandler endDocument];
304}
305
306- (void)_parseFromData:(NSData *)_data systemId:(NSString *)_sysId {
307  NSAutoreleasePool *pool;
308
309  if ([_data length] == 0) {
310    [self _handleEmptyDataInSystemId:_sysId];
311    return;
312  }
313
314  pool = [[NSAutoreleasePool alloc] init];
315
316  /* parse into structure */
317  [self setupParserWithDocumentPath:_sysId];
318  [self pushBytes:[_data bytes] count:[_data length]];
319  [self pushEOF];
320
321  if (self->doc == NULL) {
322    NSLog(@"Could not parse HTML file: %@", _sysId);
323    [self tearDownParser];
324  }
325  else {
326    [self walkDocumentTree:self->doc];
327    [self tearDownParser];
328  }
329
330  [pool release];
331}
332
333- (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
334  NSAutoreleasePool *pool;
335
336  pool = [[NSAutoreleasePool alloc] init];
337
338  if ([_source isKindOfClass:[NSData class]]) {
339    [self _parseFromData:_source systemId:_sysId];
340    return;
341  }
342  if ([_source isKindOfClass:[NSString class]]) {
343    [self _parseFromData:[_source dataUsingEncoding:NSISOLatin1StringEncoding]
344          systemId:_sysId];
345    return;
346  }
347  if ([_source isKindOfClass:[NSURL class]]) {
348    NSData *data;
349
350    data = [_source isFileURL]
351      ? (NSData *)[NSData dataWithContentsOfMappedFile:[_source path]]
352      : [_source resourceDataUsingCache:YES];
353
354    [self _parseFromData:data systemId:[_source absoluteString]];
355    return;
356  }
357
358  {
359    SaxParseException *e;
360    NSDictionary      *ui;
361
362    ui = [[NSDictionary alloc] initWithObjectsAndKeys:
363				 _source ? _source : (id)@"<nil>", @"source",
364			         self,                             @"parser",
365			       nil];
366
367    e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
368                               reason:@"can not handle data-source"
369                               userInfo:ui];
370    [ui release]; ui = nil;
371
372    [self->errorHandler fatalError:e];
373  }
374
375  [self tearDownParser];
376
377  [pool release];
378}
379- (void)parseFromSource:(id)_source {
380  if ([_source isKindOfClass:[NSString class]])
381    [self parseFromSource:_source systemId:@"<string>"];
382  else if ([_source isKindOfClass:[NSData class]])
383    [self parseFromSource:_source systemId:@"<data>"];
384  else if ([_source isKindOfClass:[NSURL class]])
385    [self parseFromSource:_source systemId:[_source absoluteString]];
386  else
387    [self parseFromSource:_source systemId:@"<memory>"];
388}
389
390- (void)parseFromSystemId:(NSString *)_sysId {
391  NSAutoreleasePool *pool;
392  NSData *data;
393
394  if (![_sysId hasPrefix:@"file://"]) {
395    /* exception */
396    return;
397  }
398
399  pool = [[NSAutoreleasePool alloc] init];
400
401  /* cut off file:// */
402  _sysId = [_sysId substringFromIndex:7];
403
404  /* load data */
405  data = [NSData dataWithContentsOfFile:_sysId];
406
407  [self _parseFromData:data systemId:_sysId];
408
409  [pool release];
410}
411
412/* process attribute nodes */
413
414- (void)processAttributes:(xmlAttrPtr)_attributes {
415  xmlAttrPtr  attribute;
416
417  /* setup or clear attribute cache */
418  if (self->attributes == nil)
419    attributes = [[SaxAttributes alloc] init];
420  else
421    [attributes clear];
422
423  if (_attributes == NULL)
424    /* nothing to process */
425    return;
426
427  /* add attributes */
428
429  for (attribute = _attributes; attribute; attribute = attribute->next) {
430    NSString *name, *xhtmlName;
431    NSString *value;
432#if 0
433    printf("attr name '%s' has NS '%s'\n",
434           attribute->name, attribute->ns ? "yes" : "no");
435#endif
436
437    name      = xmlCharsToString(attribute->name);
438    xhtmlName = [name lowercaseString];
439    value     = @"";
440
441    if (attribute->children) {
442      xmlChar  *t;
443
444      if ((t = xmlNodeListGetString(doc, attribute->children, 0))) {
445        value = xmlCharsToString(t);
446	free(t); /* should be xmlFree ?? */
447      }
448    }
449
450    [attributes addAttribute:xhtmlName
451                uri:self->namespaceURI
452                rawName:name
453                type:@"CDATA" value:value];
454
455    [name  release]; name  = nil;
456    [value release]; value = nil;
457  }
458
459  return;
460}
461
462/* walking the tree, generating SAX events */
463
464- (BOOL)processEntityRefNode:(xmlNodePtr)node {
465  NSLog(@"Ignoring entity ref: '%s'\n", node->name);
466  return YES;
467}
468
469- (BOOL)processDocumentNode:(xmlNodePtr)node {
470  BOOL result;
471
472  [self->contentHandler startDocument];
473  [self->contentHandler startPrefixMapping:@"" uri:self->namespaceURI];
474  result = [self processChildren:node->children];
475  [self->contentHandler endPrefixMapping:@""];
476  [self->contentHandler endDocument];
477
478  return result;
479}
480
481- (BOOL)processTextNode:(xmlNodePtr)_node {
482  static unichar c = '\0';
483  xmlChar  *chars;
484  NSUInteger len;
485
486  if (self->contentHandler == nil)
487    return YES;
488
489  if (_node->content == NULL) {
490    [self->contentHandler characters:&c length:0];
491    return YES;
492  }
493
494  if (self->encodeEntities) {
495    /* should use the HTML encoding routine (htmlEncodeEntities) ??? */
496
497    chars = xmlEncodeEntitiesReentrant(self->doc, _node->content);
498  }
499  else
500    chars = _node->content;
501
502  if (chars == NULL) {
503    [self->contentHandler characters:&c length:0];
504    return YES;
505  }
506  if ((len = strlen((char *)chars)) == 0) {
507    unichar c = '\0';
508    [self->contentHandler characters:&c length:0];
509    return YES;
510  }
511
512  {
513    void *data, *ts;
514
515    data = ts = calloc(len + 2, sizeof(unichar)); /* GC ?! */
516
517    if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
518                     (void *)&ts, ts + (len * sizeof(unichar)))) {
519      NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
520            __PRETTY_FUNCTION__, __LINE__);
521      if (data) free(data);
522      return NO;
523    }
524
525    len = (ts - data) / 2;
526    [self->contentHandler characters:data length:len];
527
528    if (data) free(data);
529  }
530
531  return YES;
532}
533
534- (BOOL)processCommentNode:(xmlNodePtr)_node {
535  unichar c = '\0';
536
537  if (self->lexicalHandler == nil)
538    return YES;
539
540  if (_node->content) {
541    xmlChar  *chars;
542
543    /* uses the HTML encoding routine !!!!!!!!!! */
544    chars = xmlEncodeEntitiesReentrant(self->doc, _node->content);
545
546    if (chars == NULL) {
547      [self->lexicalHandler comment:&c length:0];
548    }
549    else {
550      unsigned len;
551
552      if ((len = strlen((char *)chars)) > 0) {
553        void *data, *ts;
554
555        data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
556
557        if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
558                         (void *)&ts, ts + (len * sizeof(unichar)))) {
559          free(data);
560          NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
561                __PRETTY_FUNCTION__, __LINE__);
562          return NO;
563        }
564
565        len = (ts - data) / 2;
566        [self->lexicalHandler comment:data length:len];
567
568        free(data);
569      }
570      else {
571        unichar c = '\0';
572        [self->lexicalHandler comment:&c length:0];
573      }
574    }
575  }
576  else
577    [self->lexicalHandler comment:&c length:0];
578
579  return YES;
580}
581
582- (BOOL)processDTDNode:(xmlNodePtr)node {
583  /* do nothing with DTD nodes .. */
584  return YES;
585}
586- (BOOL)processEntityNode:(xmlNodePtr)node {
587  /* do nothing with entity nodes .. */
588  NSLog(@"%s:%i: ignoring entity node ..", __PRETTY_FUNCTION__, __LINE__);
589  return YES;
590}
591- (BOOL)processPINode:(xmlNodePtr)node {
592  /* do nothing with PI nodes .. */
593  return YES;
594}
595
596- (BOOL)processElementNode:(xmlNodePtr)node {
597  NSString *tagName, *xhtmlName;
598  BOOL     result;
599
600  self->depth++;
601
602  tagName   = xmlCharsToString(node->name);
603  xhtmlName = [tagName lowercaseString];
604
605  [self processAttributes:node->properties];
606
607  [self->contentHandler
608       startElement:xhtmlName
609       namespace:self->namespaceURI
610       rawName:tagName
611       attributes:self->attributes];
612
613  [self->attributes clear];
614
615  result = [self processChildren:node->children];
616
617  [self->contentHandler
618       endElement:xhtmlName
619       namespace:self->namespaceURI
620       rawName:tagName];
621
622  self->depth--;
623
624  [tagName release];
625  return result;
626}
627
628- (BOOL)processChildren:(xmlNodePtr)children {
629  xmlNodePtr node;
630
631  if (children == NULL)
632    return YES;
633
634  for (node = children; node; node = node->next) {
635    [self processNode:node];
636  }
637
638  return YES;
639}
640
641- (BOOL)processNode:(xmlNodePtr)_node {
642  switch(_node->type) {
643    case XML_ELEMENT_NODE:
644      return [self processElementNode:_node];
645
646    case XML_ATTRIBUTE_NODE:
647      NSLog(@"invalid place for attribute-node !");
648      return NO;
649
650    case HTML_TEXT_NODE:
651      return [self processTextNode:_node];
652
653    case XML_CDATA_SECTION_NODE:
654      return [self processTextNode:_node];
655
656    case HTML_ENTITY_REF_NODE:
657      return [self processEntityRefNode:_node];
658
659    case XML_ENTITY_NODE:
660      return [self processEntityNode:_node];
661
662    case XML_PI_NODE:
663      return [self processPINode:_node];
664
665    case HTML_COMMENT_NODE:
666      return [self processCommentNode:_node];
667
668    case XML_HTML_DOCUMENT_NODE:
669      return [self processDocumentNode:_node];
670
671    case XML_DTD_NODE:
672      return [self processDTDNode:_node];
673
674    default:
675      NSLog(@"WARNING: UNKNOWN node type %i\n", _node->type);
676      break;
677  }
678  return NO;
679}
680
681- (BOOL)walkDocumentTree:(xmlDocPtr)_doc {
682  int  type;
683  BOOL result;
684
685  type = ((xmlDocPtr)self->doc)->type;
686  ((xmlDocPtr)self->doc)->type = XML_HTML_DOCUMENT_NODE;
687
688  result = [self processNode:(xmlNodePtr)self->doc];
689
690  ((xmlDocPtr)self->doc)->type = type;
691
692  return result;
693}
694
695/* callbacks */
696
697static SaxParseException *
698mkException(libxmlHTMLSAXDriver *self, NSString *key,
699            const char *msg, va_list va)
700{
701  NSString          *s, *reason;
702  NSDictionary      *ui;
703  SaxParseException *e;
704  int count = 0, i;
705  id  keys[7], values[7];
706  id  tmp;
707  NSRange r;
708
709  s = [NSString stringWithCString:msg];
710  s = [[[NSString alloc]
711                  initWithFormat:s arguments:va]
712                  autorelease];
713
714  r = [s rangeOfString:@"\n"];
715  reason = (r.length > 0)
716    ? [s substringToIndex:r.location]
717    : s;
718
719  if ([reason length] == 0)
720    reason = @"unknown reason";
721
722  keys[0] = @"parser"; values[0] = self; count++;
723  keys[1] = @"depth";
724  values[1] = [NSNumber numberWithInt:self->depth]; count++;
725
726  if ([s length] > 0) {
727    keys[count]   = @"errorMessage";
728    values[count] = s;
729    count++;
730  }
731
732  // NSLog(@"locator: %@", self->locator);
733
734  if ((i = [self->locator lineNumber]) >= 0) {
735    keys[count] = @"line";
736    values[count] = [NSNumber numberWithInt:i];
737    count++;
738  }
739  if ((i = [self->locator columnNumber]) >= 0) {
740    keys[count] = @"column";
741    values[count] = [NSNumber numberWithInt:i];
742    count++;
743  }
744  if ((tmp = [self->locator publicId])) {
745    keys[count]   = @"publicId";
746    values[count] = tmp;
747    count++;
748  }
749  if ((tmp = [self->locator systemId])) {
750    keys[count]   = @"systemId";
751    values[count] = tmp;
752    count++;
753  }
754
755  ui = [NSDictionary dictionaryWithObjects:values forKeys:keys count:count];
756
757  e = (id)[SaxParseException exceptionWithName:key
758                             reason:reason
759                             userInfo:ui];
760  return e;
761}
762
763static void warning(void *udata, const char *msg, ...) {
764  va_list           args;
765  SaxParseException *e;
766
767  if (activeDriver == nil) {
768    NSLog(@"ERROR(%s): no driver is active !", __PRETTY_FUNCTION__);
769    return;
770  }
771
772  va_start(args, msg);
773  e = mkException(activeDriver, @"SAXWarning", msg, args);
774  va_end(args);
775
776  [activeDriver->errorHandler warning:e];
777}
778
779static void error(void *udata, const char *msg, ...) {
780  va_list           args;
781  SaxParseException *e;
782
783  if (!reportInvalidTags && msg != NULL) {
784    if (toupper((unsigned char) msg[0]) == 'T') {
785      if (strncasecmp(tagInvalidMsg, msg, strlen(tagInvalidMsg)) == 0)
786        return;
787    }
788#if 0
789    else if (toupper((unsigned char) msg[0]) == 'U') {
790      if (strncasecmp(unexpectedNobrCloseMsg, msg,
791                      strlen(unexpectedNobrCloseMsg)) == 0)
792        return;
793      printf("MSG: '%s'\n", msg);
794    }
795#endif
796  }
797  if (!reportUnclosedEntities && msg != NULL && toupper((unsigned char) msg[0]) == 'H') {
798    if (strncasecmp(unclosedEntityInvalidMsg, msg,
799                    strlen(unclosedEntityInvalidMsg)) == 0)
800      return;
801  }
802
803  if (activeDriver == nil) {
804    NSLog(@"ERROR(%s): no driver is active !", __PRETTY_FUNCTION__);
805    return;
806  }
807
808  /* msg is a format, eg 'tag %s is invalid' */
809
810  va_start(args, msg);
811  e = mkException(activeDriver, @"SAXError", msg, args);
812  va_end(args);
813
814  [activeDriver->errorHandler error:e];
815}
816
817static void fatalError(void *udata, const char *msg, ...) {
818  va_list           args;
819  SaxParseException *e;
820
821  if (activeDriver == nil) {
822    NSLog(@"ERROR(%s): no driver is active !", __PRETTY_FUNCTION__);
823    return;
824  }
825
826  va_start(args, msg);
827  e = mkException(activeDriver, @"SAXFatalError", msg, args);
828  va_end(args);
829
830  [activeDriver->errorHandler fatalError:e];
831}
832
833static void setLocator(void *udata, xmlSAXLocatorPtr _locator) {
834  if (activeDriver == nil) {
835    NSLog(@"ERROR(%s): no driver is active !", __PRETTY_FUNCTION__);
836    return;
837  }
838
839  [activeDriver->locator release];
840
841  activeDriver->locator = [[libxmlSAXLocator alloc]
842                                             initWithSaxLocator:_locator
843                                             parser:activeDriver];
844  activeDriver->locator->ctx = activeDriver->ctxt;
845
846  [activeDriver->contentHandler setDocumentLocator:activeDriver->locator];
847}
848
849@end /* libxmlHTMLSAXDriver */
850
851#include "unicode.h"
852