1/******************************************************************/
2/* Implementation of XML document processing using libxml2 */
3/* Author: Olivier Bertrand 2007-2016 */
4/******************************************************************/
5#include "my_global.h"
6#include <string.h>
7#include <stdio.h>
8#include <libxml/parser.h>
9#include <libxml/tree.h>
10#include <libxml/xpath.h>
11#include <libxml/xpathInternals.h>
12#include <libxml/catalog.h>
13#include <libxml/xmlschemastypes.h>
14#include <libxml/relaxng.h>
15
16#if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
17#error "tree support not compiled in"
18#endif
19
20#if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED)
21#error "XPath not supported"
22#endif
23
24#include "global.h"
25#include "plgdbsem.h"
26#include "xobject.h"
27#include "libdoc.h"
28
29#include "sql_string.h"
30
31/******************************************************************/
32/* Declaration of XML document processing using libxml2 */
33/* Author: Olivier Bertrand 2007-2012 */
34/******************************************************************/
35#include "plgxml.h"
36
37typedef class LIBXMLDOC *PXDOC2;
38typedef class XML2NODE *PNODE2;
39typedef class XML2ATTR *PATTR2;
40typedef class XML2NODELIST *PLIST2;
41
42/******************************************************************/
43/* XML2 block. Must have the same layout than FBLOCK up to Type. */
44/******************************************************************/
45typedef struct _x2block { /* Loaded XML file block */
46 struct _x2block *Next;
47 LPCSTR Fname; /* Point on file name */
48 size_t Length; /* Used to tell if read mode */
49 short Count; /* Nb of times file is used */
50 short Type; /* TYPE_FB_XML */
51 int Retcode; /* Return code from Load */
52 xmlDocPtr Docp; /* Document interface pointer */
53 } X2BLOCK, *PX2BLOCK;
54
55/******************************************************************/
56/* Declaration of libxml2 document. */
57/******************************************************************/
58class LIBXMLDOC : public XMLDOCUMENT {
59 friend class XML2NODE;
60 friend class XML2ATTR;
61 public:
62 // Constructor
63 LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp);
64
65 // Properties
66 virtual short GetDocType(void) {return TYPE_FB_XML2;}
67 virtual void *GetDocPtr(void) {return Docp;}
68 virtual void SetNofree(bool b) {Nofreelist = b;}
69
70 // Methods
71 virtual bool Initialize(PGLOBAL g, PCSZ entry, bool zipped);
72 virtual bool ParseFile(PGLOBAL g, char *fn);
73 virtual bool NewDoc(PGLOBAL g, PCSZ ver);
74 virtual void AddComment(PGLOBAL g, char *com);
75 virtual PXNODE GetRoot(PGLOBAL g);
76 virtual PXNODE NewRoot(PGLOBAL g, char *name);
77 virtual PXNODE NewPnode(PGLOBAL g, char *name);
78 virtual PXATTR NewPattr(PGLOBAL g);
79 virtual PXLIST NewPlist(PGLOBAL g);
80 virtual int DumpDoc(PGLOBAL g, char *ofn);
81 virtual void CloseDoc(PGLOBAL g, PFBLOCK xp);
82 virtual PFBLOCK LinkXblock(PGLOBAL g, MODE m, int rc, char *fn);
83
84 protected:
85// bool CheckDocument(FILE *of, xmlNodePtr np);
86 xmlNodeSetPtr GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp);
87 int Decode(xmlChar *cnt, char *buf, int n);
88 xmlChar *Encode(PGLOBAL g, char *txt);
89
90 // Members
91 xmlDocPtr Docp;
92 xmlNodeSetPtr Nlist;
93 xmlXPathContextPtr Ctxp;
94 xmlXPathObjectPtr Xop;
95 xmlXPathObjectPtr NlXop;
96 xmlErrorPtr Xerr;
97 char *Buf; // Temporary
98 bool Nofreelist;
99}; // end of class LIBXMLDOC
100
101/******************************************************************/
102/* Declaration of libxml2 node. */
103/******************************************************************/
104class XML2NODE : public XMLNODE {
105 friend class LIBXMLDOC;
106 friend class XML2NODELIST;
107 public:
108 // Properties
109 virtual char *GetName(PGLOBAL g) {return (char*)Nodep->name;}
110 virtual int GetType(void);
111 virtual PXNODE GetNext(PGLOBAL g);
112 virtual PXNODE GetChild(PGLOBAL g);
113
114 // Methods
115 virtual RCODE GetContent(PGLOBAL g, char *buf, int len);
116 virtual bool SetContent(PGLOBAL g, char *txtp, int len);
117 virtual PXNODE Clone(PGLOBAL g, PXNODE np);
118 virtual PXLIST GetChildElements(PGLOBAL g, char *xp, PXLIST lp);
119 virtual PXLIST SelectNodes(PGLOBAL g, char *xp, PXLIST lp);
120 virtual PXNODE SelectSingleNode(PGLOBAL g, char *xp, PXNODE np);
121 virtual PXATTR GetAttribute(PGLOBAL g, char *name, PXATTR ap);
122 virtual PXNODE AddChildNode(PGLOBAL g, PCSZ name, PXNODE np);
123 virtual PXATTR AddProperty(PGLOBAL g, char *name, PXATTR ap);
124 virtual void AddText(PGLOBAL g, PCSZ txtp);
125 virtual void DeleteChild(PGLOBAL g, PXNODE dnp);
126
127 protected:
128 // Constructor
129 XML2NODE(PXDOC dp, xmlNodePtr np);
130
131 // Members
132 xmlDocPtr Docp;
133 xmlChar *Content;
134 xmlNodePtr Nodep;
135}; // end of class XML2NODE
136
137/******************************************************************/
138/* Declaration of libxml2 node list. */
139/******************************************************************/
140class XML2NODELIST : public XMLNODELIST {
141 friend class LIBXMLDOC;
142 friend class XML2NODE;
143 public:
144 // Methods
145 virtual int GetLength(void);
146 virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
147 virtual bool DropItem(PGLOBAL g, int n);
148
149 protected:
150 // Constructor
151 XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp);
152
153 // Members
154 xmlNodeSetPtr Listp;
155}; // end of class XML2NODELIST
156
157/******************************************************************/
158/* Declaration of libxml2 attribute. */
159/******************************************************************/
160class XML2ATTR : public XMLATTRIBUTE {
161 friend class LIBXMLDOC;
162 friend class XML2NODE;
163 public:
164 // Properties
165 virtual char *GetName(PGLOBAL g) {return (char*)Atrp->name;}
166 virtual PXATTR GetNext(PGLOBAL g);
167
168 // Methods
169 virtual RCODE GetText(PGLOBAL g, char *bufp, int len);
170 virtual bool SetText(PGLOBAL g, char *txtp, int len);
171
172 protected:
173 // Constructor
174 XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np);
175
176 // Members
177 xmlAttrPtr Atrp;
178 xmlNodePtr Parent;
179}; // end of class XML2ATTR
180
181
182
183extern "C" {
184extern char version[];
185} // "C"
186
187#if defined(MEMORY_TRACE)
188static int m = 0;
189static char s[500];
190/**************************************************************************/
191/* Tracing output function. */
192/**************************************************************************/
193void xtrc(char const *fmt, ...)
194 {
195 va_list ap;
196 va_start (ap, fmt);
197 ;
198//vfprintf(stderr, fmt, ap);
199 vsprintf(s, fmt, ap);
200 if (s[strlen(s)-1] == '\n')
201 s[strlen(s)-1] = 0;
202 va_end (ap);
203 } // end of htrc
204
205static xmlFreeFunc Free;
206static xmlMallocFunc Malloc;
207static xmlMallocFunc MallocA;
208static xmlReallocFunc Realloc;
209static xmlStrdupFunc Strdup;
210
211void xmlMyFree(void *mem)
212{
213 if (trace(1)) {
214 htrc("%.4d Freeing at %p %s\n", ++m, mem, s);
215 *s = 0;
216 } // endif trace
217 Free(mem);
218} // end of xmlMyFree
219
220void *xmlMyMalloc(size_t size)
221{
222 void *p = Malloc(size);
223 if (trace(1)) {
224 htrc("%.4d Allocating %.5d at %p %s\n", ++m, size, p, s);
225 *s = 0;
226 } // endif trace
227 return p;
228} // end of xmlMyMalloc
229
230void *xmlMyMallocAtomic(size_t size)
231{
232 void *p = MallocA(size);
233 if (trace(1)) {
234 htrc("%.4d Atom alloc %.5d at %p %s\n", ++m, size, p, s);
235 *s = 0;
236 } // endif trace
237 return p;
238} // end of xmlMyMallocAtomic
239
240void *xmlMyRealloc(void *mem, size_t size)
241{
242 void *p = Realloc(mem, size);
243 if (trace(1)) {
244 htrc("%.4d ReAlloc %.5d to %p from %p %s\n", ++m, size, p, mem, s);
245 *s = 0;
246 } // endif trace
247 return p;
248} // end of xmlMyRealloc
249
250char *xmlMyStrdup(const char *str)
251{
252 char *p = Strdup(str);
253 if (trace(1)) {
254 htrc("%.4d Duplicating to %p from %p %s %s\n", ++m, p, str, str, s);
255 *s = 0;
256 } // endif trace
257 return p;
258} // end of xmlMyStrdup
259#define htrc xtrc
260#endif // MEMORY_TRACE
261
262/******************************************************************/
263/* Return a LIBXMLDOC as a XMLDOC. */
264/******************************************************************/
265PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf,
266 char *enc, PFBLOCK fp)
267 {
268 return (PXDOC) new(g) LIBXMLDOC(nsl, nsdf, enc, fp);
269 } // end of GetLibxmlDoc
270
271/******************************************************************/
272/* XML library initialization function. */
273/******************************************************************/
274void XmlInitParserLib(void)
275 {
276#if defined(MEMORY_TRACE)
277int rc = xmlGcMemGet(&Free, &Malloc, &MallocA, &Realloc, &Strdup);
278
279if (!rc)
280 rc = xmlGcMemSetup(xmlMyFree,
281 xmlMyMalloc,
282 xmlMyMallocAtomic,
283 xmlMyRealloc,
284 xmlMyStrdup);
285
286#endif // MEMORY_TRACE
287 xmlInitParser();
288 } // end of XmlInitParserLib
289
290/******************************************************************/
291/* XML library cleanup function. */
292/******************************************************************/
293/*
294 This is a copy of xmlCleanupParser() from the libxml2 sources
295 with xmlResetLastError() commented.
296
297 xmlResetLastError() called from the original xmlCleanupParser() causes
298 valgrind to report memory leaks. This happens because
299 ha_initialize_handlerton() is called from the main thread in mysqld.cc,
300 while ha_finalize_handlerton() is called from a non-main thread.
301 libxml2 gets confused because of xmlInitParser() and xmlCleanupParser()
302 being called from the different threads.
303
304 Perhaps the code in mysqld.cc should eventually be modified
305 to shutdown plugins from the main thread.
306*/
307static void
308xmlCleanupParser_replacement(void)
309 {
310 xmlCleanupCharEncodingHandlers();
311#ifdef LIBXML_CATALOG_ENABLED
312 xmlCatalogCleanup();
313#endif
314 xmlDictCleanup();
315 xmlCleanupInputCallbacks();
316#ifdef LIBXML_OUTPUT_ENABLED
317 xmlCleanupOutputCallbacks();
318#endif
319#ifdef LIBXML_SCHEMAS_ENABLED
320 xmlSchemaCleanupTypes();
321 xmlRelaxNGCleanupTypes();
322#endif
323 //xmlResetLastError();
324 xmlCleanupGlobals();
325 xmlCleanupThreads(); /* must be last if called not from the main thread */
326 xmlCleanupMemory();
327 }
328
329
330void XmlCleanupParserLib(void)
331 {
332 xmlCleanupParser_replacement();
333 } // end of XmlCleanupParserLib
334
335/******************************************************************/
336/* Close a loaded libxml2 XML file. */
337/******************************************************************/
338void CloseXML2File(PGLOBAL g, PFBLOCK fp, bool all)
339 {
340 PX2BLOCK xp = (PX2BLOCK)fp;
341
342 if (trace(1))
343 htrc("CloseXML2File: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
344
345 if (xp && xp->Count > 1 && !all) {
346 xp->Count--;
347 } else if (xp && xp->Count > 0) {
348 xmlFreeDoc(xp->Docp);
349 xp->Count = 0;
350 } // endif
351
352 } // end of CloseXML2File
353
354/* ---------------------- class LIBXMLDOC ----------------------- */
355
356/******************************************************************/
357/* LIBXMLDOC constructor. */
358/******************************************************************/
359LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp)
360 : XMLDOCUMENT(nsl, nsdf, enc)
361 {
362 assert (!fp || fp->Type == TYPE_FB_XML2);
363 Docp = (fp) ? ((PX2BLOCK)fp)->Docp : NULL;
364 Nlist = NULL;
365 Ctxp = NULL;
366 Xop = NULL;
367 NlXop = NULL;
368 Xerr = NULL;
369 Buf = NULL;
370 Nofreelist = false;
371 } // end of LIBXMLDOC constructor
372
373/******************************************************************/
374/* Initialize XML parser and check library compatibility. */
375/******************************************************************/
376bool LIBXMLDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped)
377{
378 if (zipped && InitZip(g, entry))
379 return true;
380
381 int n = xmlKeepBlanksDefault(1);
382 return MakeNSlist(g);
383} // end of Initialize
384
385/******************************************************************/
386/* Parse the XML file and construct node tree in memory. */
387/******************************************************************/
388bool LIBXMLDOC::ParseFile(PGLOBAL g, char *fn)
389 {
390 if (trace(1))
391 htrc("ParseFile\n");
392
393 if (zip) {
394 // Parse an in memory document
395 char *xdoc = GetMemDoc(g, fn);
396
397 Docp = (xdoc) ? xmlParseDoc((const xmlChar *)xdoc) : NULL;
398 } else
399 Docp = xmlParseFile(fn);
400
401 if (Docp) {
402 if (Docp->encoding)
403 Encoding = (char*)Docp->encoding;
404
405 return false;
406 } else if ((Xerr = xmlGetLastError()))
407 xmlResetError(Xerr);
408
409 return true;
410 } // end of ParseFile
411
412/******************************************************************/
413/* Create or reuse an Xblock for this document. */
414/******************************************************************/
415PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
416 {
417 PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
418 PX2BLOCK xp = (PX2BLOCK)PlugSubAlloc(g, NULL, sizeof(X2BLOCK));
419
420 memset(xp, 0, sizeof(X2BLOCK));
421 xp->Next = (PX2BLOCK)dup->Openlist;
422 dup->Openlist = (PFBLOCK)xp;
423 xp->Type = TYPE_FB_XML2;
424 xp->Fname = (LPCSTR)PlugDup(g, fn);
425 xp->Count = 1;
426 xp->Length = (m == MODE_READ) ? 1 : 0;
427 xp->Retcode = rc;
428 xp->Docp = Docp;
429
430 // Return xp as a fp
431 return (PFBLOCK)xp;
432 } // end of LinkXblock
433
434/******************************************************************/
435/* Construct and add the XML processing instruction node. */
436/******************************************************************/
437bool LIBXMLDOC::NewDoc(PGLOBAL g, PCSZ ver)
438 {
439 if (trace(1))
440 htrc("NewDoc\n");
441
442 return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL);
443 } // end of NewDoc
444
445/******************************************************************/
446/* Add a new comment node to the document. */
447/******************************************************************/
448void LIBXMLDOC::AddComment(PGLOBAL g, char *txtp)
449 {
450 if (trace(1))
451 htrc("AddComment: %s\n", txtp);
452
453 xmlNodePtr cp = xmlNewDocComment(Docp, BAD_CAST txtp);
454 xmlAddChild((xmlNodePtr)Docp, cp);
455 } // end of AddText
456
457/******************************************************************/
458/* Return the node class of the root of the document. */
459/******************************************************************/
460PXNODE LIBXMLDOC::GetRoot(PGLOBAL g)
461 {
462 if (trace(1))
463 htrc("GetRoot\n");
464
465 xmlNodePtr root = xmlDocGetRootElement(Docp);
466
467 if (!root)
468 return NULL;
469
470 return new(g) XML2NODE(this, root);
471 } // end of GetRoot
472
473/******************************************************************/
474/* Create a new root element and return its class node. */
475/******************************************************************/
476PXNODE LIBXMLDOC::NewRoot(PGLOBAL g, char *name)
477 {
478 if (trace(1))
479 htrc("NewRoot: %s\n", name);
480
481 xmlNodePtr root = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL);
482
483 if (root) {
484 xmlDocSetRootElement(Docp, root);
485 return new(g) XML2NODE(this, root);
486 } else
487 return NULL;
488
489 } // end of NewRoot
490
491/******************************************************************/
492/* Return a void XML2NODE node class. */
493/******************************************************************/
494PXNODE LIBXMLDOC::NewPnode(PGLOBAL g, char *name)
495 {
496 if (trace(1))
497 htrc("NewNode: %s\n", name);
498
499 xmlNodePtr nop;
500
501 if (name) {
502 nop = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL);
503
504 if (nop == NULL)
505 return NULL;
506
507 } else
508 nop = NULL;
509
510 return new(g) XML2NODE(this, nop);
511 } // end of NewPnode
512
513/******************************************************************/
514/* Return a void XML2ATTR node class. */
515/******************************************************************/
516PXATTR LIBXMLDOC::NewPattr(PGLOBAL g)
517 {
518 return new(g) XML2ATTR(this, NULL, NULL);
519 } // end of NewPattr
520
521/******************************************************************/
522/* Return a void XML2ATTR node class. */
523/******************************************************************/
524PXLIST LIBXMLDOC::NewPlist(PGLOBAL g)
525 {
526 return new(g) XML2NODELIST(this, NULL);
527 } // end of NewPlist
528
529/******************************************************************/
530/* Dump the node tree to a new XML file. */
531/******************************************************************/
532int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn)
533 {
534 int rc = 0;
535 FILE *of;
536
537 if (trace(1))
538 htrc("DumpDoc: %s\n", ofn);
539
540 if (!(of= global_fopen(g, MSGID_CANNOT_OPEN, ofn, "w")))
541 return -1;
542
543#if 1
544 // This function does not crash (
545 if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) {
546 xmlErrorPtr err = xmlGetLastError();
547 strcpy(g->Message, (err) ? err->message : "Error saving XML doc");
548 xmlResetError(Xerr);
549 rc = -1;
550 } // endif Save
551// rc = xmlDocDump(of, Docp);
552#else // 0
553 // Until this function is fixed, do the job ourself
554 xmlNodePtr Rootp;
555
556 // Save the modified document
557 fprintf(of, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", Encoding);
558 fprintf(of, "<!-- Created by CONNECT %s -->\n", version);
559
560 if (!(Rootp = xmlDocGetRootElement(Docp)))
561 return 1;
562
563 Buf = (char*)PlugSubAlloc(g, NULL, 1024);
564 rc = iconv_close(Cd2);
565 Cd2 = iconv_open(Encoding, "UTF-8");
566 rc = CheckDocument(of, Rootp);
567#endif // 0
568
569 fclose(of);
570 return rc;
571 } // end of Dump
572
573/******************************************************************/
574/* Free the document, cleanup the XML library, and */
575/* debug memory for regression tests. */
576/******************************************************************/
577void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp)
578 {
579 if (trace(1))
580 htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
581
582//if (xp && xp->Count == 1) {
583 if (xp) {
584 if (Nlist) {
585 xmlXPathFreeNodeSet(Nlist);
586
587 if ((Xerr = xmlGetLastError()))
588 xmlResetError(Xerr);
589
590 Nlist = NULL;
591 } // endif Nlist
592
593 if (Xop) {
594 xmlXPathFreeObject(Xop);
595
596 if ((Xerr = xmlGetLastError()))
597 xmlResetError(Xerr);
598
599 Xop = NULL;
600 } // endif Xop
601
602 if (NlXop) {
603 xmlXPathFreeObject(NlXop);
604
605 if ((Xerr = xmlGetLastError()))
606 xmlResetError(Xerr);
607
608 NlXop = NULL;
609 } // endif NlXop
610
611 if (Ctxp) {
612 xmlXPathFreeContext(Ctxp);
613
614 if ((Xerr = xmlGetLastError()))
615 xmlResetError(Xerr);
616
617 Ctxp = NULL;
618 } // endif Ctxp
619
620 } // endif xp
621
622 CloseXML2File(g, xp, false);
623 CloseZip();
624 } // end of Close
625
626/******************************************************************/
627/* Evaluate the passed Xpath from the passed context node. */
628/******************************************************************/
629xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp)
630 {
631 xmlNodeSetPtr nl;
632
633 if (trace(1))
634 htrc("GetNodeList: %s np=%p\n", xp, np);
635
636 if (!Ctxp) {
637 // Init Xpath
638 if (trace(1))
639 htrc("Calling xmlPathInit\n");
640
641 xmlXPathInit();
642
643 if (trace(1))
644 htrc("Calling xmlXPathNewContext Docp=%p\n", Docp);
645
646 // Create xpath evaluation context
647 if (!(Ctxp = xmlXPathNewContext(Docp))) {
648 strcpy(g->Message, MSG(XPATH_CNTX_ERR));
649
650 if (trace(1))
651 htrc("Context error: %s\n", g->Message);
652
653 return NULL;
654 } // endif xpathCtx
655
656 // Register namespaces from list (if any)
657 for (PNS nsp = Namespaces; nsp; nsp = nsp->Next) {
658 if (trace(1))
659 htrc("Calling xmlXPathRegisterNs Prefix=%s Uri=%s\n",
660 nsp->Prefix, nsp->Uri);
661
662 if (xmlXPathRegisterNs(Ctxp, BAD_CAST nsp->Prefix,
663 BAD_CAST nsp->Uri)) {
664 sprintf(g->Message, MSG(REGISTER_ERR), nsp->Prefix, nsp->Uri);
665
666 if (trace(1))
667 htrc("Ns error: %s\n", g->Message);
668
669 return NULL;
670 } // endif Registering
671
672 } // endfor nsp
673
674 } // endif Ctxp
675
676 if (Xop) {
677 if (trace(1))
678 htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n",
679 Xop, Nofreelist);
680
681 if (Nofreelist) {
682 // Making Nlist that must not be freed yet
683// xmlXPathFreeNodeSetList(Xop); // Caused memory leak
684 assert(!NlXop);
685 NlXop = Xop; // Freed on closing
686 Nofreelist = false;
687 } else
688 xmlXPathFreeObject(Xop); // Caused node not found
689
690 if ((Xerr = xmlGetLastError())) {
691 strcpy(g->Message, Xerr->message);
692 xmlResetError(Xerr);
693 return NULL;
694 } // endif Xerr
695
696 } // endif Xop
697
698 // Set the context to the calling node
699 Ctxp->node = np;
700
701 if (trace(1))
702 htrc("Calling xmlXPathEval %s Ctxp=%p\n", xp, Ctxp);
703
704 // Evaluate table xpath
705 if (!(Xop = xmlXPathEval(BAD_CAST xp, Ctxp))) {
706 sprintf(g->Message, MSG(XPATH_EVAL_ERR), xp);
707
708 if (trace(1))
709 htrc("Path error: %s\n", g->Message);
710
711 return NULL;
712 } else
713 nl = Xop->nodesetval;
714
715 if (trace(1))
716 htrc("GetNodeList nl=%p n=%p\n", nl, (nl) ? nl->nodeNr : 0);
717
718 return nl;
719 } // end of GetNodeList
720
721#if 0 // Not used anymore
722/******************************************************************/
723/* CheckDocument: check if the document is ok to dump. */
724/* Currently this does the dumping of the document. */
725/******************************************************************/
726bool LIBXMLDOC::CheckDocument(FILE *of, xmlNodePtr np)
727 {
728 int n;
729 bool b;
730
731 if (!np)
732 return true;
733
734 if (np->type == XML_ELEMENT_NODE) {
735 n = fprintf(of, "<%s", np->name);
736 b = CheckDocument(of, (xmlNodePtr)np->properties);
737
738 if (np->children)
739 n = fprintf(of, ">");
740 else
741 n = fprintf(of, "/>");
742
743 } else if (np->type == XML_ATTRIBUTE_NODE)
744 n = fprintf(of, " %s=\"", np->name);
745 else if (np->type == XML_TEXT_NODE)
746 n = fprintf(of, "%s", Encode(NULL, (char*)np->content));
747 else if (np->type == XML_COMMENT_NODE)
748 n = fprintf(of, "%s", Encode(NULL, (char*)np->content));
749
750 b = CheckDocument(of, np->children);
751
752 if (np->type == XML_ATTRIBUTE_NODE)
753 n = fprintf(of, "\"");
754 else if (!b && np->type == XML_ELEMENT_NODE)
755 n = fprintf(of, "</%s>", np->name);
756
757 b = CheckDocument(of, np->next);
758 return false;
759 } // end of CheckDocument
760
761/******************************************************************/
762/* Convert node or attribute content to latin characters. */
763/******************************************************************/
764int LIBXMLDOC::Decode(xmlChar *cnt, char *buf, int n)
765 {
766 const char *txt = (const char *)cnt;
767 uint dummy_errors;
768 uint32 len= copy_and_convert(buf, n, &my_charset_utf8_general_ci, txt,
769 strlen(txt), &my_charset_utf8_general_ci,
770 &dummy_errors);
771 buf[len]= '\0';
772 return 0;
773 } // end of Decode
774
775/******************************************************************/
776/* Convert node or attribute content to latin characters. */
777/******************************************************************/
778xmlChar *LIBXMLDOC::Encode(PGLOBAL g, char *txt)
779 {
780 const CHARSET_INFO *ics= &my_charset_utf8_general_ci;
781 const CHARSET_INFO *ocs= &my_charset_utf8_general_ci;
782 size_t i = strlen(txt);
783 size_t o = i * ocs->mbmaxlen / ics->mbmaxlen + 1;
784 char *buf;
785 if (g) {
786 buf = (char*)PlugSubAlloc(g, NULL, o);
787 } else {
788 o = 1024;
789 buf = Buf;
790 } // endif g
791 uint dummy_errors;
792 uint32 len= copy_and_convert(buf, o, ocs,
793 txt, i, ics,
794 &dummy_errors);
795 buf[len]= '\0';
796 return BAD_CAST buf;
797 } // end of Encode
798#endif // 0
799
800/* ---------------------- class XML2NODE ------------------------ */
801
802/******************************************************************/
803/* XML2NODE constructor. */
804/******************************************************************/
805XML2NODE::XML2NODE(PXDOC dp, xmlNodePtr np) : XMLNODE(dp)
806 {
807 Docp = ((PXDOC2)dp)->Docp;
808 Content = NULL;
809 Nodep = np;
810 } // end of XML2NODE constructor
811
812int XML2NODE::GetType(void)
813 {
814 if (trace(1))
815 htrc("GetType type=%d\n", Nodep->type);
816
817 return Nodep->type;
818 } // end of GetType
819
820/******************************************************************/
821/* Return the node class of next sibling of the node. */
822/******************************************************************/
823PXNODE XML2NODE::GetNext(PGLOBAL g)
824 {
825 if (trace(1))
826 htrc("GetNext\n");
827
828 if (!Nodep->next)
829 Next = NULL;
830 else // if (!Next)
831 Next = new(g) XML2NODE(Doc, Nodep->next);
832
833 return Next;
834 } // end of GetNext
835
836/******************************************************************/
837/* Return the node class of first children of the node. */
838/******************************************************************/
839PXNODE XML2NODE::GetChild(PGLOBAL g)
840 {
841 if (trace(1))
842 htrc("GetChild\n");
843
844 if (!Nodep->children)
845 Children = NULL;
846 else // if (!Children)
847 Children = new(g) XML2NODE(Doc, Nodep->children);
848
849 return Children;
850 } // end of GetChild
851
852/******************************************************************/
853/* Return the content of a node and subnodes. */
854/******************************************************************/
855RCODE XML2NODE::GetContent(PGLOBAL g, char *buf, int len)
856 {
857 RCODE rc = RC_OK;
858
859 if (trace(1))
860 htrc("GetContent\n");
861
862 if (Content)
863 xmlFree(Content);
864
865 if ((Content = xmlNodeGetContent(Nodep))) {
866 char *p1 = (char*)Content, *p2 = buf;
867 bool b = false;
868
869 // Copy content eliminating extra characters
870 for (; *p1; p1++)
871 if ((p2 - buf) < len) {
872 if (strchr(" \t\r\n", *p1)) {
873 if (b) {
874 // This to have one blank between sub-nodes
875 *p2++ = ' ';
876 b = false;
877 } // endif b
878
879 } else {
880 *p2++ = *p1;
881 b = true;
882 } // endif p1
883
884 } else {
885 sprintf(g->Message, "Truncated %s content", Nodep->name);
886 rc = RC_INFO;
887 } // endif len
888
889 *p2 = 0;
890
891 if (trace(1))
892 htrc("GetText buf='%s' len=%d\n", buf, len);
893
894 xmlFree(Content);
895 Content = NULL;
896 } else
897 *buf = '\0';
898
899 if (trace(1))
900 htrc("GetContent: %s\n", buf);
901
902 return rc;
903 } // end of GetContent
904
905/******************************************************************/
906/* Set the content of a node. */
907/******************************************************************/
908bool XML2NODE::SetContent(PGLOBAL g, char *txtp, int len)
909 {
910 if (trace(1))
911 htrc("SetContent: %s\n", txtp);
912
913 xmlChar *buf = xmlEncodeEntitiesReentrant(Docp, BAD_CAST txtp);
914
915 if (trace(1))
916 htrc("SetContent: %s -> %s\n", txtp, buf);
917
918 xmlNodeSetContent(Nodep, buf);
919 xmlFree(buf);
920 return false;
921 } // end of SetContent
922
923/******************************************************************/
924/* Return a clone of this node. */
925/******************************************************************/
926PXNODE XML2NODE::Clone(PGLOBAL g, PXNODE np)
927 {
928 if (trace(1))
929 htrc("Clone: np=%p\n", np);
930
931 if (np) {
932 ((PNODE2)np)->Nodep = Nodep;
933 return np;
934 } else
935 return new(g) XML2NODE(Doc, Nodep);
936
937 } // end of Clone
938
939/******************************************************************/
940/* Return the list of all or matching children that are elements.*/
941/******************************************************************/
942PXLIST XML2NODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp)
943 {
944 if (trace(1))
945 htrc("GetChildElements: %s\n", xp);
946
947 return SelectNodes(g, (xp) ? xp : (char*)"*", lp);
948 } // end of GetChildElements
949
950/******************************************************************/
951/* Return the list of nodes verifying the passed Xpath. */
952/******************************************************************/
953PXLIST XML2NODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp)
954 {
955 if (trace(1))
956 htrc("SelectNodes: %s\n", xp);
957
958 xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp);
959
960 if (lp) {
961 ((PLIST2)lp)->Listp = nl;
962 return lp;
963 } else
964 return new(g) XML2NODELIST(Doc, nl);
965
966 } // end of SelectNodes
967
968/******************************************************************/
969/* Return the first node verifying the passed Xapth. */
970/******************************************************************/
971PXNODE XML2NODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np)
972 {
973 if (trace(1))
974 htrc("SelectSingleNode: %s\n", xp);
975
976 xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp);
977
978 if (nl && nl->nodeNr) {
979 if (np) {
980 ((PNODE2)np)->Nodep = nl->nodeTab[0];
981 return np;
982 } else
983 return new(g) XML2NODE(Doc, nl->nodeTab[0]);
984
985 } else
986 return NULL;
987
988 } // end of SelectSingleNode
989
990/******************************************************************/
991/* Return the node attribute with the specified name. */
992/******************************************************************/
993PXATTR XML2NODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap)
994 {
995 xmlAttrPtr atp;
996
997 if (trace(1))
998 htrc("GetAttribute: %s\n", SVP(name));
999
1000 if (name)
1001 atp = xmlHasProp(Nodep, BAD_CAST name);
1002 else
1003 atp = Nodep->properties;
1004
1005
1006 if (atp) {
1007 if (ap) {
1008 ((PATTR2)ap)->Atrp = atp;
1009 ((PATTR2)ap)->Parent = Nodep;
1010 return ap;
1011 } else
1012 return new(g) XML2ATTR(Doc, atp, Nodep);
1013
1014 } else
1015 return NULL;
1016
1017 } // end of GetAttribute
1018
1019/******************************************************************/
1020/* Add a new child node to this node and return it. */
1021/******************************************************************/
1022PXNODE XML2NODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np)
1023 {
1024 char *p, *pn, *pf = NULL, *nmp = PlugDup(g, name);
1025
1026 if (trace(1))
1027 htrc("AddChildNode: %s\n", name);
1028
1029 // Is a prefix specified
1030 if ((pn = strchr(nmp, ':'))) {
1031 pf = nmp;
1032 *pn++ = '\0'; // Separate name from prefix
1033 } else
1034 pn = nmp;
1035
1036 // If name has the format m[n] only m is taken as node name
1037 if ((p = strchr(pn, '[')))
1038 p = BufAlloc(g, pn, p - pn);
1039 else
1040 p = pn;
1041
1042 xmlNodePtr nop = xmlNewChild(Nodep, NULL, BAD_CAST p, NULL);
1043
1044 if (!nop)
1045 return NULL;
1046
1047 if (pf) {
1048 // Prefixed name, is it the default NS prefix?
1049 if (Doc->DefNs && !strcmp(pf, Doc->DefNs))
1050 pf = NULL; // Default namespace
1051
1052 xmlNsPtr nsp = xmlSearchNs(Docp, nop, BAD_CAST pf);
1053
1054 if (!nsp)
1055 nsp = xmlNewNs(nop, NULL, BAD_CAST pf);
1056
1057 // Set node namespace
1058 nop->ns = nsp;
1059 *(--p) = ':'; // Restore Xname
1060 } else if (Doc->DefNs && xmlSearchNs(Docp, nop, NULL))
1061 // Not in default namespace
1062 nop->ns = xmlNewNs(nop, BAD_CAST "", NULL);
1063
1064 if (np)
1065 ((PNODE2)np)->Nodep = nop;
1066 else
1067 np = new(g) XML2NODE(Doc, nop);
1068
1069 return NewChild(np);
1070 } // end of AddChildNode
1071
1072/******************************************************************/
1073/* Add a new property to this node and return it. */
1074/******************************************************************/
1075PXATTR XML2NODE::AddProperty(PGLOBAL g, char *name, PXATTR ap)
1076 {
1077 if (trace(1))
1078 htrc("AddProperty: %s\n", name);
1079
1080 xmlAttrPtr atp = xmlNewProp(Nodep, BAD_CAST name, NULL);
1081
1082 if (atp) {
1083 if (ap) {
1084 ((PATTR2)ap)->Atrp = atp;
1085 ((PATTR2)ap)->Parent = Nodep;
1086 return ap;
1087 } else
1088 return new(g) XML2ATTR(Doc, atp, Nodep);
1089
1090 } else
1091 return NULL;
1092
1093 } // end of AddProperty
1094
1095/******************************************************************/
1096/* Add a new text node to this node. */
1097/******************************************************************/
1098void XML2NODE::AddText(PGLOBAL g, PCSZ txtp)
1099 {
1100 if (trace(1))
1101 htrc("AddText: %s\n", txtp);
1102
1103 // This is to avoid a blank line when inserting a new line
1104 xmlNodePtr np = xmlGetLastChild(Nodep);
1105
1106 if (np && np->type == XML_TEXT_NODE) {
1107 xmlUnlinkNode(np);
1108 xmlFreeNode(np);
1109 } // endif type
1110
1111 // Add the new text
1112 xmlAddChild(Nodep, xmlNewText(BAD_CAST txtp));
1113 } // end of AddText
1114
1115/******************************************************************/
1116/* Remove a child node from this node. */
1117/******************************************************************/
1118void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
1119 {
1120 xmlErrorPtr xerr;
1121
1122 if (trace(1))
1123 htrc("DeleteChild: node=%p\n", dnp);
1124
1125 xmlNodePtr np = ((PNODE2)dnp)->Nodep;
1126 xmlNodePtr text = np->next;
1127
1128 // This is specific to row nodes
1129 if (text && text->type == XML_TEXT_NODE) {
1130 xmlUnlinkNode(text);
1131
1132 if ((xerr = xmlGetLastError()))
1133 goto err;
1134
1135 xmlFreeNode(text);
1136
1137 if ((xerr = xmlGetLastError()))
1138 goto err;
1139
1140 } // endif type
1141
1142 xmlUnlinkNode(np);
1143
1144 if ((xerr = xmlGetLastError()))
1145 goto err;
1146
1147 xmlFreeNode(np);
1148
1149 if ((xerr = xmlGetLastError()))
1150 goto err;
1151
1152 Delete(dnp);
1153
1154 if ((xerr = xmlGetLastError()))
1155 goto err;
1156
1157 return;
1158
1159err:
1160 if (trace(1))
1161 htrc("DeleteChild: errmsg=%s\n", xerr->message);
1162
1163 xmlResetError(xerr);
1164 } // end of DeleteChild
1165
1166/* -------------------- class XML2NODELIST ---------------------- */
1167
1168/******************************************************************/
1169/* XML2NODELIST constructor. */
1170/******************************************************************/
1171XML2NODELIST::XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp)
1172 : XMLNODELIST(dp)
1173 {
1174 Listp = lp;
1175 } // end of XML2NODELIST constructor
1176
1177/******************************************************************/
1178/* Return the length of the list. */
1179/******************************************************************/
1180int XML2NODELIST::GetLength(void)
1181 {
1182 return (Listp) ? Listp->nodeNr : 0;
1183 } // end of GetLength
1184
1185/******************************************************************/
1186/* Return the nth element of the list. */
1187/******************************************************************/
1188PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
1189 {
1190 if (trace(1))
1191 htrc("GetItem: %d\n", n);
1192
1193 if (!Listp || Listp->nodeNr <= n)
1194 return NULL;
1195
1196 if (np) {
1197 ((PNODE2)np)->Nodep = Listp->nodeTab[n];
1198 return np;
1199 } else
1200 return new(g) XML2NODE(Doc, Listp->nodeTab[n]);
1201
1202 } // end of GetItem
1203
1204/******************************************************************/
1205/* Reset the pointer on the deleted item. */
1206/******************************************************************/
1207bool XML2NODELIST::DropItem(PGLOBAL g, int n)
1208 {
1209 if (trace(1))
1210 htrc("DropItem: n=%d\n", n);
1211
1212 // We should do something here
1213 if (!Listp || Listp->nodeNr <= n)
1214 return true;
1215
1216 Listp->nodeTab[n] = NULL; // This was causing Valgrind warning
1217 return false;
1218 } // end of DropItem
1219
1220/* ---------------------- class XML2ATTR ------------------------ */
1221
1222/******************************************************************/
1223/* XML2ATTR constructor. */
1224/******************************************************************/
1225XML2ATTR::XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np)
1226 : XMLATTRIBUTE(dp)
1227 {
1228 Atrp = ap;
1229 Parent = np;
1230 } // end of XML2ATTR constructor
1231
1232/******************************************************************/
1233/* Return the next sibling of the attribute. */
1234/******************************************************************/
1235PXATTR XML2ATTR::GetNext(PGLOBAL g)
1236 {
1237 if (trace(1))
1238 htrc("Attr GetNext\n");
1239
1240 if (!Atrp->next)
1241 return NULL;
1242 else
1243 return new(g) XML2ATTR(Doc, Atrp->next, Atrp->parent);
1244
1245 } // end of GetNext
1246
1247/******************************************************************/
1248/* Return the text of an attribute. */
1249/******************************************************************/
1250RCODE XML2ATTR::GetText(PGLOBAL g, char *buf, int len)
1251 {
1252 RCODE rc = RC_OK;
1253 xmlChar *txt;
1254
1255 if (trace(1))
1256 htrc("GetText\n");
1257
1258 if ((txt = xmlGetProp(Atrp->parent, Atrp->name))) {
1259 // Copy the text to the buffer
1260 if (strlen((char*)txt) >= (unsigned)len) {
1261 memcpy(buf, txt, len - 1);
1262 buf[len - 1] = 0;
1263 sprintf(g->Message, "Truncated %s content", Atrp->name);
1264 rc = RC_INFO;
1265 } else
1266 strcpy(buf, (const char*)txt);
1267
1268 xmlFree(txt);
1269 } else
1270 *buf = '\0';
1271
1272 if (trace(1))
1273 htrc("GetText: %s\n", buf);
1274
1275 return rc;
1276 } // end of GetText
1277
1278/******************************************************************/
1279/* Set the content of an attribute. */
1280/******************************************************************/
1281bool XML2ATTR::SetText(PGLOBAL g, char *txtp, int len)
1282 {
1283 if (trace(1))
1284 htrc("SetText: %s %d\n", txtp, len);
1285
1286 xmlSetProp(Parent, Atrp->name, BAD_CAST txtp);
1287 return false;
1288 } // end of SetText
1289