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