Coverity #1099209, #1099221, #1099225 fixed
[scilab.git] / scilab / modules / xml / src / cpp / XMLDocument.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 #include "XMLObject.hxx"
17 #include "XMLDocument.hxx"
18 #include "XMLElement.hxx"
19 #include "XMLXPath.hxx"
20 #include "XMLValidation.hxx"
21 #include "XMLValidationRelaxNG.hxx"
22 #include "VariableScope.hxx"
23
24 extern "C"
25 {
26 #include "expandPathVariable.h"
27 #include "sci_malloc.h"
28 #include "localization.h"
29 }
30
31 #include <iostream>
32
33 #define BUFFER_SIZE 1024
34
35 namespace org_modules_xml
36 {
37
38 std::string XMLDocument::errorBuffer;
39 std::string XMLDocument::errorXPathBuffer;
40 std::list < XMLDocument *> XMLDocument::openDocs;
41
42 XMLDocument::XMLDocument(const char *path, bool validate, std::string * error, const char * encoding, const bool html): XMLObject()
43 {
44     char *expandedPath = expandPathVariable(const_cast<char *>(path));
45     if (expandedPath)
46     {
47         if (html)
48         {
49             document = readHTMLDocument(const_cast<const char *>(expandedPath), encoding, error);
50         }
51         else
52         {
53             document = readDocument(const_cast<const char *>(expandedPath), encoding, validate, error);
54         }
55
56         FREE(expandedPath);
57         if (document)
58         {
59             openDocs.push_back(this);
60             scope->registerPointers(document, this);
61         }
62     }
63     else
64     {
65         document = 0;
66         *error = std::string(gettext("Invalid file name: ")) + std::string(path);
67     }
68
69     id = scope->getVariableId(*this);
70     scilabType = XMLDOCUMENT;
71 }
72
73 XMLDocument::XMLDocument(const std::string & xmlCode, bool validate, std::string * error, const char * encoding, const bool html): XMLObject()
74 {
75     if (html)
76     {
77         document = readHTMLDocument(xmlCode, encoding, error);
78     }
79     else
80     {
81         document = readDocument(xmlCode, encoding, validate, error);
82     }
83
84     if (document)
85     {
86         openDocs.push_back(this);
87     }
88     scope->registerPointers(document, this);
89     id = scope->getVariableId(*this);
90     scilabType = XMLDOCUMENT;
91 }
92
93 XMLDocument::XMLDocument(char *uri, char *version): XMLObject()
94 {
95     char *newUri = 0;
96     char *expandedPath = 0;
97
98     if (!version)
99     {
100         version = const_cast < char *>("1.0");
101     }
102     document = xmlNewDoc((xmlChar *) version);
103     openDocs.push_back(this);
104     scope->registerPointers(document, this);
105     id = scope->getVariableId(*this);
106     scilabType = XMLDOCUMENT;
107
108     expandedPath = expandPathVariable(const_cast < char *>(uri));
109
110     if (expandedPath)
111     {
112         newUri = (char *)xmlMalloc(sizeof(char) * (strlen(expandedPath) + 1));
113         memcpy(newUri, expandedPath, sizeof(char) * (strlen(expandedPath) + 1));
114         document->URL = (xmlChar *) newUri;
115         FREE(expandedPath);
116     }
117 }
118
119 XMLDocument::~XMLDocument()
120 {
121     scope->unregisterPointer(document);
122     scope->removeId(id);
123     if (document)
124     {
125         openDocs.remove(this);
126         if (openDocs.size() == 0 && XMLValidation::getOpenValidationFiles().size() == 0)
127         {
128             resetScope();
129         }
130         xmlFreeDoc(document);
131     }
132
133 #ifdef SCILAB_DEBUG_XML
134     for (std::set<XMLObject *>::const_iterator i = XMLObject::pointers.begin(), e = XMLObject::pointers.end(); i != e; ++i)
135     {
136         XMLObject * p = *i;
137         if (p != this)
138         {
139             std::cout << "Stay = " << (void*)p << ":" << typeid(*p).name() << std::endl;
140         }
141     }
142 #endif
143 }
144
145 void *XMLDocument::getRealXMLPointer() const
146 {
147     return static_cast < void *>(document);
148 }
149
150 const XMLXPath *XMLDocument::makeXPathQuery(const char *query, char **namespaces, int length, const XMLElement * e, std::string * error)
151 {
152     errorXPathBuffer.clear();
153
154     xmlXPathContext *ctxt = xmlXPathNewContext(document);
155
156     if (!ctxt)
157     {
158         errorXPathBuffer.append(gettext("Cannot create a parser context"));
159         *error = errorXPathBuffer;
160         return 0;
161     }
162
163     if (e)
164     {
165         ctxt->node = (xmlNode *) e->getRealXMLPointer();
166     }
167
168     if (namespaces)
169     {
170         for (int i = 0; i < length; i++)
171         {
172             xmlXPathRegisterNs(ctxt, (const xmlChar *)namespaces[i], (const xmlChar *)namespaces[i + length]);
173         }
174     }
175
176     xmlSetStructuredErrorFunc(ctxt, XMLDocument::errorXPathFunction);
177     xmlXPathCompExpr *expr = xmlXPathCtxtCompile(ctxt, (const xmlChar *)query);
178
179     if (!expr)
180     {
181         xmlSetStructuredErrorFunc(ctxt, 0);
182         xmlXPathFreeContext(ctxt);
183         *error = errorXPathBuffer;
184         return 0;
185     }
186
187     xmlXPathObject *xpath = xmlXPathCompiledEval(expr, ctxt);
188
189     xmlSetStructuredErrorFunc(ctxt, 0);
190     xmlXPathFreeContext(ctxt);
191     xmlXPathFreeCompExpr(expr);
192     if (!xpath)
193     {
194         *error = errorXPathBuffer;
195         return 0;
196     }
197
198     return new XMLXPath(*this, xpath);
199 }
200
201 const XMLObject *XMLDocument::getXMLObjectParent() const
202 {
203     return 0;
204 }
205
206 const std::string XMLDocument::toString() const
207 {
208     std::ostringstream oss;
209
210     oss << "XML Document" << std::endl
211         << "url: " << getDocumentURL() << std::endl
212         << "root: " << "XML Element";
213
214     return oss.str();
215 }
216
217 const std::string XMLDocument::dump(bool indent) const
218 {
219     xmlChar *buffer = 0;
220     int size = 0;
221     xmlDocDumpFormatMemory(document, &buffer, &size, indent ? 1 : 0);
222     std::string str((const char *)buffer);
223     xmlFree(buffer);
224
225     return str;
226 }
227
228 const std::string XMLDocument::dumpHTML(bool indent) const
229 {
230     xmlBuffer * buffer = xmlBufferCreate();
231     int ret;
232     int options = XML_SAVE_AS_HTML;
233     if (indent)
234     {
235         options |= XML_SAVE_FORMAT;
236     }
237
238     xmlThrDefIndentTreeOutput(1);
239     xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buffer, 0, options);
240     ret = xmlSaveDoc(ctxt, document);
241     xmlSaveFlush(ctxt);
242     xmlSaveClose(ctxt);
243
244     std::string str((const char *)xmlBufferDetach(buffer));
245     xmlBufferFree(buffer);
246
247     return str;
248 }
249
250 const XMLElement *XMLDocument::getRoot() const
251 {
252     xmlNode *root = xmlDocGetRootElement(document);
253     if (!root)
254     {
255         return 0;
256     }
257
258     XMLObject *obj = scope->getXMLObjectFromLibXMLPtr(root);
259
260     if (obj)
261     {
262         return static_cast < XMLElement * >(obj);
263     }
264
265     return new XMLElement(*this, root);
266 }
267
268 void XMLDocument::setRoot(const XMLElement & elem) const
269 {
270     xmlNode *root = xmlDocGetRootElement(document);
271     if (root != elem.getRealNode())
272     {
273         xmlNode *cpy = xmlCopyNodeList(elem.getRealNode());
274         xmlUnlinkNode(cpy);
275         xmlDocSetRootElement(document, cpy);
276     }
277 }
278
279 void XMLDocument::setRoot(const std::string & xmlCode, std::string * error) const
280 {
281     XMLDocument doc = XMLDocument(xmlCode, false, error);
282
283     if (error->empty())
284     {
285         setRoot(*doc.getRoot());
286     }
287 }
288
289 const char *XMLDocument::getDocumentURL() const
290 {
291     if (document->URL)
292     {
293         return (const char *)document->URL;
294     }
295     else
296     {
297         return "Undefined";
298     }
299 }
300
301 void XMLDocument::setDocumentURL(const std::string & url) const
302 {
303     char *expandedPath = 0;
304     char *newURL = 0;
305     expandedPath = expandPathVariable(const_cast < char *>(url.c_str()));
306
307     if (expandedPath)
308     {
309         xmlFree((void *)document->URL);
310         newURL = (char *)xmlMalloc(sizeof(char) * (strlen(expandedPath) + 1));
311         memcpy(newURL, expandedPath, sizeof(char) * (strlen(expandedPath) + 1));
312         document->URL = (xmlChar *) newURL;
313         FREE(expandedPath);
314     }
315 }
316
317 const std::list < XMLDocument * >&XMLDocument::getOpenDocuments()
318 {
319     return openDocs;
320 }
321
322 void XMLDocument::closeAllDocuments()
323 {
324     int size = (int)openDocs.size();
325     XMLDocument **arr = new XMLDocument *[size];
326     int j = 0;
327
328     for (std::list < XMLDocument * >::iterator i = openDocs.begin(); i != openDocs.end(); i++, j++)
329     {
330         arr[j] = *i;
331     }
332     for (j = 0; j < size; j++)
333     {
334         delete arr[j];
335     }
336     delete[]arr;
337 }
338
339 xmlDoc *XMLDocument::readDocument(const char *filename, const char * encoding, bool validate, std::string * error)
340 {
341     xmlParserCtxt *ctxt = initContext(error, validate);
342     xmlDoc *doc = 0;
343     int options = XML_PARSE_NSCLEAN | XML_PARSE_NOBLANKS;
344
345     if (validate)
346     {
347         options |= XML_PARSE_DTDVALID;
348     }
349
350     if (!ctxt)
351     {
352         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
353         return 0;
354     }
355
356     doc = xmlCtxtReadFile(ctxt, filename, encoding, options);
357     if (!doc || !ctxt->valid)
358     {
359         *error = errorBuffer;
360     }
361
362     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
363     xmlFreeParserCtxt(ctxt);
364
365     return doc;
366 }
367
368 xmlDoc *XMLDocument::readHTMLDocument(const char *filename, const char * encoding, std::string * error)
369 {
370     htmlParserCtxt *ctxt = initHTMLContext(error);
371     htmlDocPtr doc = 0;
372     int options = HTML_PARSE_NOWARNING | HTML_PARSE_NOBLANKS | HTML_PARSE_COMPACT;
373
374     if (!ctxt)
375     {
376         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
377         return 0;
378     }
379
380     doc = htmlCtxtReadFile(ctxt, filename, encoding, options);
381     if (!doc || !ctxt->valid)
382     {
383         *error = errorBuffer;
384     }
385
386     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
387     htmlFreeParserCtxt(ctxt);
388
389     return (xmlDoc *)doc;
390 }
391
392 xmlDoc *XMLDocument::readDocument(const std::string & xmlCode, const char * encoding, bool validate, std::string * error)
393 {
394     xmlParserCtxt *ctxt = initContext(error, validate);
395     xmlDoc *doc = 0;
396     int options = XML_PARSE_NSCLEAN | XML_PARSE_NOBLANKS;
397
398     if (validate)
399     {
400         options |= XML_PARSE_DTDVALID;
401     }
402
403     if (!ctxt)
404     {
405         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
406         return 0;
407     }
408
409     doc = xmlCtxtReadDoc(ctxt, (const xmlChar *)xmlCode.c_str(), 0, encoding, options);
410     if (!doc || !ctxt->valid)
411     {
412         *error = errorBuffer;
413     }
414
415     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
416     xmlFreeParserCtxt(ctxt);
417
418     return doc;
419 }
420
421 xmlDoc *XMLDocument::readHTMLDocument(const std::string & htmlCode, const char * encoding, std::string * error)
422 {
423     htmlParserCtxt *ctxt = initHTMLContext(error);
424     htmlDocPtr doc = 0;
425     int options = HTML_PARSE_NOWARNING | HTML_PARSE_NOBLANKS | HTML_PARSE_COMPACT;
426
427     if (!ctxt)
428     {
429         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
430         return 0;
431     }
432
433     doc = htmlCtxtReadDoc(ctxt, (const xmlChar *)htmlCode.c_str(), 0, encoding, options);
434     if (!doc || !ctxt->valid)
435     {
436         *error = errorBuffer;
437     }
438
439     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
440     htmlFreeParserCtxt(ctxt);
441
442     return (xmlDoc *)doc;
443 }
444
445 bool XMLDocument::saveToFile(const std::string & filename, const bool indent) const
446 {
447     xmlThrDefIndentTreeOutput(1);
448     return xmlSaveFormatFile(filename.c_str(), document, indent) != -1;
449 }
450
451 bool XMLDocument::saveToHTMLFile(const std::string & filename, const bool indent) const
452 {
453     int ret;
454     int options = XML_SAVE_AS_HTML;
455     if (indent)
456     {
457         options |= XML_SAVE_FORMAT;
458     }
459
460     xmlThrDefIndentTreeOutput(1);
461     xmlSaveCtxtPtr ctxt = xmlSaveToFilename(filename.c_str(), 0, options);
462     ret = xmlSaveDoc(ctxt, document);
463     xmlSaveFlush(ctxt);
464     xmlSaveClose(ctxt);
465
466     return ret != -1;
467 }
468
469 xmlParserCtxt *XMLDocument::initContext(std::string * error, bool validate)
470 {
471     xmlParserCtxt *ctxt;
472
473     errorXPathBuffer.clear();
474
475     ctxt = xmlNewParserCtxt();
476     if (!ctxt)
477     {
478         errorBuffer.append(gettext("Cannot create a parser context"));
479         *error = errorBuffer;
480         return 0;
481     }
482
483     if (validate)
484     {
485         ctxt->vctxt.error = (xmlValidityErrorFunc) errorFunction;
486     }
487
488     xmlSetGenericErrorFunc(ctxt, errorFunction);
489
490     return ctxt;
491 }
492
493 htmlParserCtxt *XMLDocument::initHTMLContext(std::string * error)
494 {
495     htmlParserCtxt *ctxt;
496
497     errorXPathBuffer.clear();
498
499     ctxt = htmlNewParserCtxt();
500     if (!ctxt)
501     {
502         errorBuffer.append(gettext("Cannot create a parser context"));
503         *error = errorBuffer;
504         return 0;
505     }
506
507     xmlSetGenericErrorFunc((xmlParserCtxt *)ctxt, errorFunction);
508
509     return ctxt;
510 }
511
512 void XMLDocument::errorFunction(void *ctx, const char *msg, ...)
513 {
514     char str[BUFFER_SIZE];
515     va_list args;
516
517     va_start(args, msg);
518     vsnprintf(str, BUFFER_SIZE, msg, args);
519     va_end(args);
520     errorBuffer.append(str);
521 }
522
523 void XMLDocument::errorXPathFunction(void *ctx, xmlError * error)
524 {
525     errorXPathBuffer.append(error->message);
526 }
527 }