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
XMLDocument(const char * path,bool validate,std::string * error,const char * encoding,const bool html)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
XMLDocument(const std::string & xmlCode,bool validate,std::string * error,const char * encoding,const bool html)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
XMLDocument(char * uri,char * version)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
~XMLDocument()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
getRealXMLPointer() const145 void *XMLDocument::getRealXMLPointer() const
146 {
147 return static_cast < void *>(document);
148 }
149
makeXPathQuery(const char * query,char ** namespaces,int length,const XMLElement * e,std::string * error)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
getXMLObjectParent() const201 const XMLObject *XMLDocument::getXMLObjectParent() const
202 {
203 return 0;
204 }
205
toString() const206 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
dump(bool indent) const217 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
dumpHTML(bool indent) const228 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
getRoot() const250 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
setRoot(const XMLElement & elem) const268 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
setRoot(const std::string & xmlCode,std::string * error) const279 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
getDocumentURL() const289 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
setDocumentURL(const std::string & url) const301 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
getOpenDocuments()317 const std::list < XMLDocument * >&XMLDocument::getOpenDocuments()
318 {
319 return openDocs;
320 }
321
closeAllDocuments()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
readDocument(const char * filename,const char * encoding,bool validate,std::string * error)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
readHTMLDocument(const char * filename,const char * encoding,std::string * error)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
readDocument(const std::string & xmlCode,const char * encoding,bool validate,std::string * error)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
readHTMLDocument(const std::string & htmlCode,const char * encoding,std::string * error)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
saveToFile(const std::string & filename,const bool indent) const445 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
saveToHTMLFile(const std::string & filename,const bool indent) const451 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
initContext(std::string * error,bool validate)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
initHTMLContext(std::string * error)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
errorFunction(void * ctx,const char * msg,...)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
errorXPathFunction(void * ctx,xmlError * error)523 void XMLDocument::errorXPathFunction(void *ctx, xmlError * error)
524 {
525 errorXPathBuffer.append(error->message);
526 }
527 }
528