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