Imported Upstream version 2.39.1
[platform/upstream/libxml++.git] / libxml++ / parsers / saxparser.cc
1 /* saxparser.cc
2  * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
3  * are covered by the GNU Lesser General Public License, which should be
4  * included with libxml++ as the file COPYING.
5  *
6  * 2002/01/05 Valentin Rusu - fixed some potential buffer overruns
7  * 2002/01/21 Valentin Rusu - added CDATA handlers
8  */
9
10 #include "libxml++/parsers/saxparser.h"
11 #include "libxml++/nodes/element.h"
12 #include "libxml++/keepblanks.h"
13
14 #include <libxml/parser.h>
15 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
16
17 #include <cstdarg> //For va_list.
18 #include <iostream>
19
20 namespace xmlpp {
21
22 struct SaxParserCallback
23 {
24   static xmlEntityPtr get_entity(void* context, const xmlChar* name);
25   static void entity_decl(void* context, const xmlChar* name, int type, const xmlChar* publicId, const xmlChar* systemId, xmlChar* content);
26   static void start_document(void* context);
27   static void end_document(void* context);
28   static void start_element(void* context, const xmlChar* name, const xmlChar** p);
29   static void end_element(void* context, const xmlChar* name);
30   static void characters(void* context, const xmlChar* ch, int len);
31   static void comment(void* context, const xmlChar* value);
32   static void warning(void* context, const char* fmt, ...);
33   static void error(void* context, const char* fmt, ...);
34   static void fatal_error(void* context, const char* fmt, ...);
35   static void cdata_block(void* context, const xmlChar* value, int len);
36   static void internal_subset(void* context, const xmlChar* name, const xmlChar*publicId, const xmlChar*systemId);
37 };
38
39
40
41 SaxParser::SaxParser(bool use_get_entity)
42   : sax_handler_( new _xmlSAXHandler )
43 {
44   xmlSAXHandler temp = {
45     SaxParserCallback::internal_subset,
46     0,  // isStandalone
47     0,  // hasInternalSubset
48     0,  // hasExternalSubset
49     0,  // resolveEntity
50     use_get_entity ? SaxParserCallback::get_entity : 0, // getEntity
51     SaxParserCallback::entity_decl, // entityDecl
52     0,  // notationDecl
53     0,  // attributeDecl
54     0,  // elementDecl
55     0,  // unparsedEntityDecl
56     0,  // setDocumentLocator
57     SaxParserCallback::start_document, // startDocument
58     SaxParserCallback::end_document, // endDocument
59     SaxParserCallback::start_element, // startElement
60     SaxParserCallback::end_element, // endElement
61     0,  // reference
62     SaxParserCallback::characters, // characters
63     0,  // ignorableWhitespace
64     0,  // processingInstruction
65     SaxParserCallback::comment,  // comment
66     SaxParserCallback::warning,  // warning
67     SaxParserCallback::error,  // error
68     SaxParserCallback::fatal_error, // fatalError
69     0,  // getParameterEntity
70     SaxParserCallback::cdata_block, // cdataBlock
71     0,  // externalSubset
72     0,  // initialized
73     0,  // private
74     0,  // startElementNs
75     0,  // endElementNs
76     0,  // serror
77   };
78   *sax_handler_ = temp;
79 }
80
81 SaxParser::~SaxParser()
82 {
83   release_underlying();
84 }
85
86 xmlEntityPtr SaxParser::on_get_entity(const Glib::ustring& name)
87 {
88   return entity_resolver_doc_.get_entity(name);
89 }
90
91 void SaxParser::on_entity_declaration(const Glib::ustring& name, XmlEntityType type, const Glib::ustring& publicId, const Glib::ustring& systemId, const Glib::ustring& content)
92 {
93   entity_resolver_doc_.set_entity_declaration(name, type, publicId, systemId, content);
94 }  
95
96 void SaxParser::on_start_document()
97 {
98 }
99
100 void SaxParser::on_end_document()
101 {
102 }
103
104 void SaxParser::on_start_element(const Glib::ustring& /* name */, const AttributeList& /* attributes */)
105 {
106 }
107
108 void SaxParser::on_end_element(const Glib::ustring& /* name */)
109 {
110 }
111
112 void SaxParser::on_characters(const Glib::ustring& /* text */)
113 {
114 }
115
116 void SaxParser::on_comment(const Glib::ustring& /* text */)
117 {
118 }
119
120 void SaxParser::on_warning(const Glib::ustring& /* text */)
121 {
122 }
123
124 void SaxParser::on_error(const Glib::ustring& /* text */)
125 {
126 }
127
128
129 void SaxParser::on_fatal_error(const Glib::ustring& text)
130 {
131   throw parse_error("Fatal error: " + text);
132 }
133
134 void SaxParser::on_cdata_block(const Glib::ustring& /* text */)
135 {
136 }
137
138 void SaxParser::on_internal_subset(const Glib::ustring& name,
139                          const Glib::ustring& publicId,
140                          const Glib::ustring& systemId)
141 {
142   entity_resolver_doc_.set_internal_subset(name, publicId, systemId);
143 }
144
145 // implementation of this function is inspired by the SAX documentation by James Henstridge.
146 // (http://www.daa.com.au/~james/gnome/xml-sax/implementing.html)
147 void SaxParser::parse()
148 {
149   //TODO If this is not the first parsing with this SaxParser, the xmlDoc object
150   // in entity_resolver_doc_ should be deleted and replaced by a new one.
151   // Otherwise entity declarations from a previous parsing may erroneously affect
152   // this parsing. This would be much easier if entity_resolver_doc_ were a
153   // std::auto_ptr<Document>, so the xmlpp::Document could be deleted and a new
154   // one created. A good place for such code would be in an overridden
155   // SaxParser::initialize_context(). It would be an ABI break.
156
157   if(!context_)
158   {
159     throw internal_error("Parser context not created.");
160   }
161
162   auto old_sax = context_->sax;
163   context_->sax = sax_handler_.get();
164
165   xmlResetLastError();
166   initialize_context();
167   
168   const int parseError = xmlParseDocument(context_);
169
170   context_->sax = old_sax;
171
172   auto error_str = format_xml_parser_error(context_);
173   if (error_str.empty() && parseError == -1)
174     error_str = "xmlParseDocument() failed.";
175
176   release_underlying(); // Free context_
177
178   check_for_exception();
179
180   if(!error_str.empty())
181   {
182     throw parse_error(error_str);
183   }
184 }
185
186 void SaxParser::parse_file(const Glib::ustring& filename)
187 {
188   if(context_)
189   {
190     throw parse_error("Attempt to start a second parse while a parse is in progress.");
191   }
192
193   KeepBlanks k(KeepBlanks::Default);
194
195   context_ = xmlCreateFileParserCtxt(filename.c_str());
196   parse();
197 }
198
199 void SaxParser::parse_memory_raw(const unsigned char* contents, size_type bytes_count)
200 {
201   if(context_)
202   {
203     throw parse_error("Attempt to start a second parse while a parse is in progress.");
204   }
205
206   KeepBlanks k(KeepBlanks::Default);
207
208   context_ = xmlCreateMemoryParserCtxt((const char*)contents, bytes_count);
209   parse();
210 }
211   
212 void SaxParser::parse_memory(const Glib::ustring& contents)
213 {
214   parse_memory_raw((const unsigned char*)contents.c_str(), contents.bytes());
215 }
216
217 void SaxParser::parse_stream(std::istream& in)
218 {
219   if(context_)
220   {
221     throw parse_error("Attempt to start a second parse while a parse is in progress.");
222   }
223
224   KeepBlanks k(KeepBlanks::Default);
225   xmlResetLastError();
226
227   context_ = xmlCreatePushParserCtxt(
228       sax_handler_.get(),
229       0, // user_data
230       0, // chunk
231       0, // size
232       0); // no filename for fetching external entities
233
234   if(!context_)
235   {
236     throw internal_error("Could not create parser context\n" + format_xml_error());
237   }
238
239   initialize_context();
240
241   //TODO: Shouldn't we use a Glib::ustring here, and some alternative to std::getline()?
242   int firstParseError = XML_ERR_OK;
243   std::string line;
244   while( ( ! exception_ )
245       && std::getline(in, line))
246   {
247     // since getline does not get the line separator, we have to add it since the parser care
248     // about layout in certain cases.
249     line += '\n';
250
251     const int parseError = xmlParseChunk(context_, line.c_str(),
252       line.size() /* This is a std::string, not a ustring, so this is the number of bytes. */,
253       0 /* don't terminate */);
254
255     // Save the first error code if any, but read on.
256     // More errors might be reported and then thrown by check_for_exception().
257     if (parseError != XML_ERR_OK && firstParseError == XML_ERR_OK)
258       firstParseError = parseError;
259   }
260
261   if( ! exception_ )
262   {
263      //This is called just to terminate parsing.
264     const int parseError = xmlParseChunk(context_, 0 /* chunk */, 0 /* size */, 1 /* terminate (1 or 0) */);
265
266     if (parseError != XML_ERR_OK && firstParseError == XML_ERR_OK)
267       firstParseError = parseError;
268   }
269
270   auto error_str = format_xml_parser_error(context_);
271   if (error_str.empty() && firstParseError != XML_ERR_OK)
272     error_str = "Error code from xmlParseChunk(): " + Glib::ustring::format(firstParseError);
273
274   release_underlying(); // Free context_
275
276   check_for_exception();
277
278   if(!error_str.empty())
279   {
280     throw parse_error(error_str);
281   }
282 }
283
284 void SaxParser::parse_chunk(const Glib::ustring& chunk)
285 {
286   parse_chunk_raw((const unsigned char*)chunk.c_str(), chunk.bytes());
287 }
288
289 void SaxParser::parse_chunk_raw(const unsigned char* contents, size_type bytes_count)
290 {
291   KeepBlanks k(KeepBlanks::Default);
292   xmlResetLastError();
293
294   if(!context_)
295   {
296     context_ = xmlCreatePushParserCtxt(
297       sax_handler_.get(),
298       0, // user_data
299       0, // chunk
300       0, // size
301       0); // no filename for fetching external entities
302
303     if(!context_)
304     {
305       throw internal_error("Could not create parser context\n" + format_xml_error());
306     }
307     initialize_context();
308   }
309   else
310     xmlCtxtResetLastError(context_);
311   
312   int parseError = XML_ERR_OK;
313   if(!exception_)
314     parseError = xmlParseChunk(context_, (const char*)contents, bytes_count, 0 /* don't terminate */);
315
316   check_for_exception();
317
318   auto error_str = format_xml_parser_error(context_);
319   if (error_str.empty() && parseError != XML_ERR_OK)
320     error_str = "Error code from xmlParseChunk(): " + Glib::ustring::format(parseError);
321   if(!error_str.empty())
322   {
323     throw parse_error(error_str);
324   }
325 }
326
327 void SaxParser::release_underlying()
328 {
329   Parser::release_underlying();
330 }
331
332 void SaxParser::finish_chunk_parsing()
333 {
334   xmlResetLastError();
335   if(!context_)
336   {
337     context_ = xmlCreatePushParserCtxt(
338       sax_handler_.get(),
339       0, // this, // user_data
340       0, // chunk
341       0, // size
342       0); // no filename for fetching external entities
343
344     if(!context_)
345     {
346       throw internal_error("Could not create parser context\n" + format_xml_error());
347     }
348     initialize_context();
349   }
350   else
351     xmlCtxtResetLastError(context_);
352
353   int parseError = XML_ERR_OK;
354   if(!exception_)
355     //This is called just to terminate parsing.
356     parseError = xmlParseChunk(context_, 0 /* chunk */, 0 /* size */, 1 /* terminate (1 or 0) */);
357
358   auto error_str = format_xml_parser_error(context_);
359   if (error_str.empty() && parseError != XML_ERR_OK)
360     error_str = "Error code from xmlParseChunk(): " + Glib::ustring::format(parseError);
361
362   release_underlying(); // Free context_
363
364   check_for_exception();
365
366   if(!error_str.empty())
367   {
368     throw parse_error(error_str);
369   }
370 }
371
372
373 xmlEntityPtr SaxParserCallback::get_entity(void* context, const xmlChar* name)
374 {
375   auto the_context = static_cast<_xmlParserCtxt*>(context);
376   auto parser = static_cast<SaxParser*>(the_context->_private);
377   xmlEntityPtr result = nullptr;
378
379   try
380   {
381     result = parser->on_get_entity((const char*)name);
382   }
383   catch(const exception& e)
384   {
385     parser->handleException(e);
386   }
387   
388   return result;
389 }
390
391 void SaxParserCallback::entity_decl(void* context, const xmlChar* name, int type, const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
392 {
393   auto the_context = static_cast<_xmlParserCtxt*>(context);
394   auto parser = static_cast<SaxParser*>(the_context->_private);
395
396   try
397   {
398     parser->on_entity_declaration(
399       ( name ? Glib::ustring((const char*)name) : ""),
400       static_cast<XmlEntityType>(type),
401       ( publicId ? Glib::ustring((const char*)publicId) : ""),
402       ( systemId ? Glib::ustring((const char*)systemId) : ""),
403       ( content ? Glib::ustring((const char*)content) : "") );
404   }
405   catch(const exception& e)
406   {
407     parser->handleException(e);
408   }
409 }
410
411 void SaxParserCallback::start_document(void* context)
412 {
413   auto the_context = static_cast<_xmlParserCtxt*>(context);
414   auto parser = static_cast<SaxParser*>(the_context->_private);
415
416   try
417   {
418     parser->on_start_document();
419   }
420   catch(const exception& e)
421   {
422     parser->handleException(e);
423   }
424 }
425
426 void SaxParserCallback::end_document(void* context)
427 {
428   auto the_context = static_cast<_xmlParserCtxt*>(context);
429   auto parser = static_cast<SaxParser*>(the_context->_private);
430
431   if(parser->exception_)
432     return;
433
434   try
435   {
436     parser->on_end_document();
437   }
438   catch(const exception& e)
439   {
440     parser->handleException(e);
441   }
442 }
443
444 void SaxParserCallback::start_element(void* context,
445                                         const xmlChar* name,
446                                         const xmlChar** p)
447 {
448   auto the_context = static_cast<_xmlParserCtxt*>(context);
449   auto parser = static_cast<SaxParser*>(the_context->_private);
450
451   SaxParser::AttributeList attributes;
452
453   if(p)
454     for(const xmlChar** cur = p; cur && *cur; cur += 2)
455       attributes.push_back(
456                           SaxParser::Attribute( (char*)*cur, (char*)*(cur + 1) ));
457
458   try
459   {
460     parser->on_start_element(Glib::ustring((const char*) name), attributes);
461   }
462   catch(const exception& e)
463   {
464     parser->handleException(e);
465   }
466 }
467
468 void SaxParserCallback::end_element(void* context, const xmlChar* name)
469 {
470   auto the_context = static_cast<_xmlParserCtxt*>(context);
471   auto parser = static_cast<SaxParser*>(the_context->_private);
472
473   try
474   {
475     parser->on_end_element(Glib::ustring((const char*) name));
476   }
477   catch(const exception& e)
478   {
479     parser->handleException(e);
480   }
481 }
482
483 void SaxParserCallback::characters(void * context, const xmlChar* ch, int len)
484 {
485   auto the_context = static_cast<_xmlParserCtxt*>(context);
486   auto parser = static_cast<SaxParser*>(the_context->_private);
487
488   try
489   {
490     // Here we force the use of Glib::ustring::ustring( InputIterator begin, InputIterator end )
491     // instead of Glib::ustring::ustring( const char*, size_type ) because it
492     // expects the length of the string in characters, not in bytes.
493     parser->on_characters(
494         Glib::ustring(
495           reinterpret_cast<const char *>(ch),
496           reinterpret_cast<const char *>(ch + len) ) );
497   }
498   catch(const exception& e)
499   {
500     parser->handleException(e);
501   }
502 }
503
504 void SaxParserCallback::comment(void* context, const xmlChar* value)
505 {
506   auto the_context = static_cast<_xmlParserCtxt*>(context);
507   auto parser = static_cast<SaxParser*>(the_context->_private);
508
509   try
510   {
511     parser->on_comment(Glib::ustring((const char*) value));
512   }
513   catch(const exception& e)
514   {
515     parser->handleException(e);
516   }
517 }
518
519 void SaxParserCallback::warning(void* context, const char* fmt, ...)
520 {
521   auto the_context = static_cast<_xmlParserCtxt*>(context);
522   auto parser = static_cast<SaxParser*>(the_context->_private);
523
524   va_list arg;
525   char buff[1024]; //TODO: Larger/Shared
526
527   va_start(arg, fmt);
528   vsnprintf(buff, sizeof(buff)/sizeof(buff[0]), fmt, arg);
529   va_end(arg);
530
531   try
532   {
533     parser->on_warning(Glib::ustring(buff));
534   }
535   catch(const exception& e)
536   {
537     parser->handleException(e);
538   }
539 }
540
541 void SaxParserCallback::error(void* context, const char* fmt, ...)
542 {
543   auto the_context = static_cast<_xmlParserCtxt*>(context);
544   auto parser = static_cast<SaxParser*>(the_context->_private);
545
546   va_list arg;
547   char buff[1024]; //TODO: Larger/Shared
548
549   if(parser->exception_)
550     return;
551
552   va_start(arg, fmt);
553   vsnprintf(buff, sizeof(buff)/sizeof(buff[0]), fmt, arg);
554   va_end(arg);
555
556   try
557   {
558     parser->on_error(Glib::ustring(buff));
559   }
560   catch(const exception& e)
561   {
562     parser->handleException(e);
563   }
564 }
565
566 void SaxParserCallback::fatal_error(void* context, const char* fmt, ...)
567 {
568   auto the_context = static_cast<_xmlParserCtxt*>(context);
569   auto parser = static_cast<SaxParser*>(the_context->_private);
570
571   va_list arg;
572   char buff[1024]; //TODO: Larger/Shared
573
574   va_start(arg, fmt);
575   vsnprintf(buff, sizeof(buff)/sizeof(buff[0]), fmt, arg);
576   va_end(arg);
577
578   try
579   {
580     parser->on_fatal_error(Glib::ustring(buff));
581   }
582   catch(const exception& e)
583   {
584     parser->handleException(e);
585   }
586 }
587
588 void SaxParserCallback::cdata_block(void* context, const xmlChar* value, int len)
589 {
590   auto the_context = static_cast<_xmlParserCtxt*>(context);
591   auto parser = static_cast<SaxParser*>(the_context->_private);
592
593   try
594   {
595     // Here we force the use of Glib::ustring::ustring( InputIterator begin, InputIterator end )
596     // see comments in SaxParserCallback::characters
597     parser->on_cdata_block(
598         Glib::ustring(
599           reinterpret_cast<const char *>(value),
600           reinterpret_cast<const char *>(value + len) ) );
601   }
602   catch(const exception& e)
603   {
604     parser->handleException(e);
605   }
606 }
607
608 void SaxParserCallback::internal_subset(void* context, const xmlChar* name,
609   const xmlChar* publicId, const xmlChar* systemId)
610 {
611   auto the_context = static_cast<_xmlParserCtxt*>(context);
612   auto parser = static_cast<SaxParser*>(the_context->_private);
613   
614   try
615   {
616     const auto pid = publicId ? Glib::ustring((const char*) publicId) : "";
617     const auto sid = systemId ? Glib::ustring((const char*) systemId) : "";
618
619     parser->on_internal_subset( Glib::ustring((const char*) name), pid, sid);
620   }
621   catch(const exception& e)
622   {
623     parser->handleException(e);
624   }
625 }
626
627 } // namespace xmlpp