diff options
Diffstat (limited to 'storage/connect/libdoc.cpp')
-rw-r--r-- | storage/connect/libdoc.cpp | 1250 |
1 files changed, 1250 insertions, 0 deletions
diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp new file mode 100644 index 00000000..67f22ce2 --- /dev/null +++ b/storage/connect/libdoc.cpp @@ -0,0 +1,1250 @@ +/******************************************************************/ +/* Implementation of XML document processing using libxml2 */ +/* Author: Olivier Bertrand 2007-2016 */ +/******************************************************************/ +#include "my_global.h" +#include <string.h> +#include <stdio.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxml/catalog.h> +#include <libxml/xmlschemastypes.h> +#include <libxml/relaxng.h> + +#if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED) +#error "tree support not compiled in" +#endif + +#if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED) +#error "XPath not supported" +#endif + +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +#include "libdoc.h" + +#include "sql_string.h" + +/******************************************************************/ +/* Declaration of XML document processing using libxml2 */ +/* Author: Olivier Bertrand 2007-2012 */ +/******************************************************************/ +#include "plgxml.h" + +typedef class LIBXMLDOC *PXDOC2; +typedef class XML2NODE *PNODE2; +typedef class XML2ATTR *PATTR2; +typedef class XML2NODELIST *PLIST2; + +/******************************************************************/ +/* XML2 block. Must have the same layout than FBLOCK up to Type. */ +/******************************************************************/ +typedef struct _x2block { /* Loaded XML file block */ + struct _x2block *Next; + LPCSTR Fname; /* Point on file name */ + size_t Length; /* Used to tell if read mode */ + short Count; /* Nb of times file is used */ + short Type; /* TYPE_FB_XML */ + int Retcode; /* Return code from Load */ + xmlDocPtr Docp; /* Document interface pointer */ + } X2BLOCK, *PX2BLOCK; + +/******************************************************************/ +/* Declaration of libxml2 document. */ +/******************************************************************/ +class LIBXMLDOC : public XMLDOCUMENT { + friend class XML2NODE; + friend class XML2ATTR; + public: + // Constructor + LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp); + + // Properties + virtual short GetDocType(void) {return TYPE_FB_XML2;} + virtual void *GetDocPtr(void) {return Docp;} + virtual void SetNofree(bool b) {Nofreelist = b;} + + // Methods + virtual bool Initialize(PGLOBAL g, PCSZ entry, bool zipped); + virtual bool ParseFile(PGLOBAL g, char *fn); + virtual bool NewDoc(PGLOBAL g, PCSZ ver); + virtual void AddComment(PGLOBAL g, char *com); + virtual PXNODE GetRoot(PGLOBAL g); + virtual PXNODE NewRoot(PGLOBAL g, char *name); + virtual PXNODE NewPnode(PGLOBAL g, char *name); + virtual PXATTR NewPattr(PGLOBAL g); + virtual PXLIST NewPlist(PGLOBAL g); + virtual int DumpDoc(PGLOBAL g, char *ofn); + virtual void CloseDoc(PGLOBAL g, PFBLOCK xp); + virtual PFBLOCK LinkXblock(PGLOBAL g, MODE m, int rc, char *fn); + + protected: +// bool CheckDocument(FILE *of, xmlNodePtr np); + xmlNodeSetPtr GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp); + int Decode(xmlChar *cnt, char *buf, int n); + xmlChar *Encode(PGLOBAL g, char *txt); + + // Members + xmlDocPtr Docp; + xmlNodeSetPtr Nlist; + xmlXPathContextPtr Ctxp; + xmlXPathObjectPtr Xop; + xmlXPathObjectPtr NlXop; + xmlErrorPtr Xerr; + char *Buf; // Temporary + bool Nofreelist; +}; // end of class LIBXMLDOC + +/******************************************************************/ +/* Declaration of libxml2 node. */ +/******************************************************************/ +class XML2NODE : public XMLNODE { + friend class LIBXMLDOC; + friend class XML2NODELIST; + public: + // Properties + virtual char *GetName(PGLOBAL g) {return (char*)Nodep->name;} + virtual int GetType(void); + virtual PXNODE GetNext(PGLOBAL g); + virtual PXNODE GetChild(PGLOBAL g); + + // Methods + virtual RCODE GetContent(PGLOBAL g, char *buf, int len); + virtual bool SetContent(PGLOBAL g, char *txtp, int len); + virtual PXNODE Clone(PGLOBAL g, PXNODE np); + virtual PXLIST GetChildElements(PGLOBAL g, char *xp, PXLIST lp); + virtual PXLIST SelectNodes(PGLOBAL g, char *xp, PXLIST lp); + virtual PXNODE SelectSingleNode(PGLOBAL g, char *xp, PXNODE np); + virtual PXATTR GetAttribute(PGLOBAL g, char *name, PXATTR ap); + virtual PXNODE AddChildNode(PGLOBAL g, PCSZ name, PXNODE np); + virtual PXATTR AddProperty(PGLOBAL g, char *name, PXATTR ap); + virtual void AddText(PGLOBAL g, PCSZ txtp); + virtual void DeleteChild(PGLOBAL g, PXNODE dnp); + + protected: + // Constructor + XML2NODE(PXDOC dp, xmlNodePtr np); + + // Members + xmlDocPtr Docp; + xmlChar *Content; + xmlNodePtr Nodep; +}; // end of class XML2NODE + +/******************************************************************/ +/* Declaration of libxml2 node list. */ +/******************************************************************/ +class XML2NODELIST : public XMLNODELIST { + friend class LIBXMLDOC; + friend class XML2NODE; + public: + // Methods + virtual int GetLength(void); + virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np); + virtual bool DropItem(PGLOBAL g, int n); + + protected: + // Constructor + XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp); + + // Members + xmlNodeSetPtr Listp; +}; // end of class XML2NODELIST + +/******************************************************************/ +/* Declaration of libxml2 attribute. */ +/******************************************************************/ +class XML2ATTR : public XMLATTRIBUTE { + friend class LIBXMLDOC; + friend class XML2NODE; + public: + // Properties + virtual char *GetName(PGLOBAL g) {return (char*)Atrp->name;} + virtual PXATTR GetNext(PGLOBAL g); + + // Methods + virtual RCODE GetText(PGLOBAL g, char *bufp, int len); + virtual bool SetText(PGLOBAL g, char *txtp, int len); + + protected: + // Constructor + XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np); + + // Members + xmlAttrPtr Atrp; + xmlNodePtr Parent; +}; // end of class XML2ATTR + + + +extern "C" { +extern char version[]; +} // "C" + +#if defined(MEMORY_TRACE) +static int m = 0; +static char s[500]; +/**************************************************************************/ +/* Tracing output function. */ +/**************************************************************************/ +void xtrc(char const *fmt, ...) + { + va_list ap; + va_start (ap, fmt); + ; +//vfprintf(stderr, fmt, ap); + vsprintf(s, fmt, ap); + if (s[strlen(s)-1] == '\n') + s[strlen(s)-1] = 0; + va_end (ap); + } // end of htrc + +static xmlFreeFunc Free; +static xmlMallocFunc Malloc; +static xmlMallocFunc MallocA; +static xmlReallocFunc Realloc; +static xmlStrdupFunc Strdup; + +void xmlMyFree(void *mem) +{ + if (trace(1)) { + htrc("%.4d Freeing at %p %-.256s\n", ++m, mem, s); + *s = 0; + } // endif trace + Free(mem); +} // end of xmlMyFree + +void *xmlMyMalloc(size_t size) +{ + void *p = Malloc(size); + if (trace(1)) { + htrc("%.4d Allocating %.5d at %p %-.256s\n", ++m, size, p, s); + *s = 0; + } // endif trace + return p; +} // end of xmlMyMalloc + +void *xmlMyMallocAtomic(size_t size) +{ + void *p = MallocA(size); + if (trace(1)) { + htrc("%.4d Atom alloc %.5d at %p %-.256s\n", ++m, size, p, s); + *s = 0; + } // endif trace + return p; +} // end of xmlMyMallocAtomic + +void *xmlMyRealloc(void *mem, size_t size) +{ + void *p = Realloc(mem, size); + if (trace(1)) { + htrc("%.4d ReAlloc %.5d to %p from %p %-.256s\n", ++m, size, p, mem, s); + *s = 0; + } // endif trace + return p; +} // end of xmlMyRealloc + +char *xmlMyStrdup(const char *str) +{ + char *p = Strdup(str); + if (trace(1)) { + htrc("%.4d Duplicating to %p from %p %-.256s %-.256s\n", ++m, p, str, str, s); + *s = 0; + } // endif trace + return p; +} // end of xmlMyStrdup +#define htrc xtrc +#endif // MEMORY_TRACE + +/******************************************************************/ +/* Return a LIBXMLDOC as a XMLDOC. */ +/******************************************************************/ +PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp) + { + return (PXDOC) new(g) LIBXMLDOC(nsl, nsdf, enc, fp); + } // end of GetLibxmlDoc + +/******************************************************************/ +/* XML library initialization function. */ +/******************************************************************/ +void XmlInitParserLib(void) + { +#if defined(MEMORY_TRACE) +int rc = xmlGcMemGet(&Free, &Malloc, &MallocA, &Realloc, &Strdup); + +if (!rc) + rc = xmlGcMemSetup(xmlMyFree, + xmlMyMalloc, + xmlMyMallocAtomic, + xmlMyRealloc, + xmlMyStrdup); + +#endif // MEMORY_TRACE + xmlInitParser(); + } // end of XmlInitParserLib + +/******************************************************************/ +/* XML library cleanup function. */ +/******************************************************************/ +void XmlCleanupParserLib(void) + { + } // end of XmlCleanupParserLib + +/******************************************************************/ +/* Close a loaded libxml2 XML file. */ +/******************************************************************/ +void CloseXML2File(PGLOBAL g, PFBLOCK fp, bool all) + { + PX2BLOCK xp = (PX2BLOCK)fp; + + if (trace(1)) + htrc("CloseXML2File: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0); + + if (xp && xp->Count > 1 && !all) { + xp->Count--; + } else if (xp && xp->Count > 0) { + xmlFreeDoc(xp->Docp); + xp->Count = 0; + } // endif + + } // end of CloseXML2File + +/* ---------------------- class LIBXMLDOC ----------------------- */ + +/******************************************************************/ +/* LIBXMLDOC constructor. */ +/******************************************************************/ +LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) + : XMLDOCUMENT(nsl, nsdf, enc) + { + assert (!fp || fp->Type == TYPE_FB_XML2); + Docp = (fp) ? ((PX2BLOCK)fp)->Docp : NULL; + Nlist = NULL; + Ctxp = NULL; + Xop = NULL; + NlXop = NULL; + Xerr = NULL; + Buf = NULL; + Nofreelist = false; + } // end of LIBXMLDOC constructor + +/******************************************************************/ +/* Initialize XML parser and check library compatibility. */ +/******************************************************************/ +bool LIBXMLDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped) +{ + if (zipped && InitZip(g, entry)) + return true; + + xmlKeepBlanksDefault(1); + return MakeNSlist(g); +} // end of Initialize + +/******************************************************************/ +/* Parse the XML file and construct node tree in memory. */ +/******************************************************************/ +bool LIBXMLDOC::ParseFile(PGLOBAL g, char *fn) + { + if (trace(1)) + htrc("ParseFile\n"); + + if (zip) { + // Parse an in memory document + char *xdoc = GetMemDoc(g, fn); + + Docp = (xdoc) ? xmlParseDoc((const xmlChar *)xdoc) : NULL; + } else + Docp = xmlParseFile(fn); + + if (Docp) { + if (Docp->encoding) + Encoding = (char*)Docp->encoding; + + return false; + } else if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + return true; + } // end of ParseFile + +/******************************************************************/ +/* Create or reuse an Xblock for this document. */ +/******************************************************************/ +PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) + { + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + PX2BLOCK xp = (PX2BLOCK)PlugSubAlloc(g, NULL, sizeof(X2BLOCK)); + + memset(xp, 0, sizeof(X2BLOCK)); + xp->Next = (PX2BLOCK)dup->Openlist; + dup->Openlist = (PFBLOCK)xp; + xp->Type = TYPE_FB_XML2; + xp->Fname = (LPCSTR)PlugDup(g, fn); + xp->Count = 1; + xp->Length = (m == MODE_READ) ? 1 : 0; + xp->Retcode = rc; + xp->Docp = Docp; + + // Return xp as a fp + return (PFBLOCK)xp; + } // end of LinkXblock + +/******************************************************************/ +/* Construct and add the XML processing instruction node. */ +/******************************************************************/ +bool LIBXMLDOC::NewDoc(PGLOBAL g, PCSZ ver) + { + if (trace(1)) + htrc("NewDoc\n"); + + return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL); + } // end of NewDoc + +/******************************************************************/ +/* Add a new comment node to the document. */ +/******************************************************************/ +void LIBXMLDOC::AddComment(PGLOBAL g, char *txtp) + { + if (trace(1)) + htrc("AddComment: %-.256s\n", txtp); + + xmlNodePtr cp = xmlNewDocComment(Docp, BAD_CAST txtp); + xmlAddChild((xmlNodePtr)Docp, cp); + } // end of AddText + +/******************************************************************/ +/* Return the node class of the root of the document. */ +/******************************************************************/ +PXNODE LIBXMLDOC::GetRoot(PGLOBAL g) + { + if (trace(1)) + htrc("GetRoot\n"); + + xmlNodePtr root = xmlDocGetRootElement(Docp); + + if (!root) + return NULL; + + return new(g) XML2NODE(this, root); + } // end of GetRoot + +/******************************************************************/ +/* Create a new root element and return its class node. */ +/******************************************************************/ +PXNODE LIBXMLDOC::NewRoot(PGLOBAL g, char *name) + { + if (trace(1)) + htrc("NewRoot: %-.256s\n", name); + + xmlNodePtr root = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL); + + if (root) { + xmlDocSetRootElement(Docp, root); + return new(g) XML2NODE(this, root); + } else + return NULL; + + } // end of NewRoot + +/******************************************************************/ +/* Return a void XML2NODE node class. */ +/******************************************************************/ +PXNODE LIBXMLDOC::NewPnode(PGLOBAL g, char *name) + { + if (trace(1)) + htrc("NewNode: %-.256s\n", name); + + xmlNodePtr nop; + + if (name) { + nop = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL); + + if (nop == NULL) + return NULL; + + } else + nop = NULL; + + return new(g) XML2NODE(this, nop); + } // end of NewPnode + +/******************************************************************/ +/* Return a void XML2ATTR node class. */ +/******************************************************************/ +PXATTR LIBXMLDOC::NewPattr(PGLOBAL g) + { + return new(g) XML2ATTR(this, NULL, NULL); + } // end of NewPattr + +/******************************************************************/ +/* Return a void XML2ATTR node class. */ +/******************************************************************/ +PXLIST LIBXMLDOC::NewPlist(PGLOBAL g) + { + return new(g) XML2NODELIST(this, NULL); + } // end of NewPlist + +/******************************************************************/ +/* Dump the node tree to a new XML file. */ +/******************************************************************/ +int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn) + { + int rc = 0; + FILE *of; + + if (trace(1)) + htrc("DumpDoc: %-.256s\n", ofn); + + if (!(of= global_fopen(g, MSGID_CANNOT_OPEN, ofn, "w"))) + return -1; + +#if 1 + // This function does not crash ( + if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) { + xmlErrorPtr err = xmlGetLastError(); + strcpy(g->Message, (err) ? err->message : "Error saving XML doc"); + xmlResetError(Xerr); + rc = -1; + } // endif Save +// rc = xmlDocDump(of, Docp); +#else // 0 + // Until this function is fixed, do the job ourself + xmlNodePtr Rootp; + + // Save the modified document + fprintf(of, "<?xml version=\"1.0\" encoding=\"%-.256s\"?>\n", Encoding); + fprintf(of, "<!-- Created by CONNECT %-.256s -->\n", version); + + if (!(Rootp = xmlDocGetRootElement(Docp))) + return 1; + + Buf = (char*)PlugSubAlloc(g, NULL, 1024); + rc = iconv_close(Cd2); + Cd2 = iconv_open(Encoding, "UTF-8"); + rc = CheckDocument(of, Rootp); +#endif // 0 + + fclose(of); + return rc; + } // end of Dump + +/******************************************************************/ +/* Free the document, cleanup the XML library, and */ +/* debug memory for regression tests. */ +/******************************************************************/ +void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) + { + if (trace(1)) + htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0); + +//if (xp && xp->Count == 1) { + if (xp) { + if (Nlist) { + xmlXPathFreeNodeSet(Nlist); + + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + Nlist = NULL; + } // endif Nlist + + if (Xop) { + xmlXPathFreeObject(Xop); + + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + Xop = NULL; + } // endif Xop + + if (NlXop) { + xmlXPathFreeObject(NlXop); + + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + NlXop = NULL; + } // endif NlXop + + if (Ctxp) { + xmlXPathFreeContext(Ctxp); + + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + Ctxp = NULL; + } // endif Ctxp + + } // endif xp + + CloseXML2File(g, xp, false); + CloseZip(); + } // end of Close + +/******************************************************************/ +/* Evaluate the passed Xpath from the passed context node. */ +/******************************************************************/ +xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp) + { + xmlNodeSetPtr nl; + + if (trace(1)) + htrc("GetNodeList: %-.256s np=%p\n", xp, np); + + if (!Ctxp) { + // Init Xpath + if (trace(1)) + htrc("Calling xmlPathInit\n"); + + xmlInitParser(); + + if (trace(1)) + htrc("Calling xmlXPathNewContext Docp=%p\n", Docp); + + // Create xpath evaluation context + if (!(Ctxp = xmlXPathNewContext(Docp))) { + strcpy(g->Message, MSG(XPATH_CNTX_ERR)); + + if (trace(1)) + htrc("Context error: %-.256s\n", g->Message); + + return NULL; + } // endif xpathCtx + + // Register namespaces from list (if any) + for (PNS nsp = Namespaces; nsp; nsp = nsp->Next) { + if (trace(1)) + htrc("Calling xmlXPathRegisterNs Prefix=%-.256s Uri=%-.512s\n", + nsp->Prefix, nsp->Uri); + + if (xmlXPathRegisterNs(Ctxp, BAD_CAST nsp->Prefix, + BAD_CAST nsp->Uri)) { + snprintf(g->Message, sizeof(g->Message), MSG(REGISTER_ERR), nsp->Prefix, nsp->Uri); + + if (trace(1)) + htrc("Ns error: %-.256s\n", g->Message); + + return NULL; + } // endif Registering + + } // endfor nsp + + } // endif Ctxp + + if (Xop) { + if (trace(1)) + htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n", + Xop, Nofreelist); + + if (Nofreelist) { + // Making Nlist that must not be freed yet +// xmlXPathFreeNodeSetList(Xop); // Caused memory leak + assert(!NlXop); + NlXop = Xop; // Freed on closing + Nofreelist = false; + } else + xmlXPathFreeObject(Xop); // Caused node not found + + if ((Xerr = xmlGetLastError())) { + strcpy(g->Message, Xerr->message); + xmlResetError(Xerr); + return NULL; + } // endif Xerr + + } // endif Xop + + // Set the context to the calling node + Ctxp->node = np; + + if (trace(1)) + htrc("Calling xmlXPathEval %-.256s Ctxp=%p\n", xp, Ctxp); + + // Evaluate table xpath + if (!(Xop = xmlXPathEval(BAD_CAST xp, Ctxp))) { + snprintf(g->Message, sizeof(g->Message), MSG(XPATH_EVAL_ERR), xp); + + if (trace(1)) + htrc("Path error: %-.256s\n", g->Message); + + return NULL; + } else + nl = Xop->nodesetval; + + if (trace(1)) + htrc("GetNodeList nl=%p n=%p\n", nl, (nl) ? nl->nodeNr : 0); + + return nl; + } // end of GetNodeList + +#if 0 // Not used anymore +/******************************************************************/ +/* CheckDocument: check if the document is ok to dump. */ +/* Currently this does the dumping of the document. */ +/******************************************************************/ +bool LIBXMLDOC::CheckDocument(FILE *of, xmlNodePtr np) + { + int n; + bool b; + + if (!np) + return true; + + if (np->type == XML_ELEMENT_NODE) { + n = fprintf(of, "<%s", np->name); + b = CheckDocument(of, (xmlNodePtr)np->properties); + + if (np->children) + n = fprintf(of, ">"); + else + n = fprintf(of, "/>"); + + } else if (np->type == XML_ATTRIBUTE_NODE) + n = fprintf(of, " %s=\"", np->name); + else if (np->type == XML_TEXT_NODE) + n = fprintf(of, "%s", Encode(NULL, (char*)np->content)); + else if (np->type == XML_COMMENT_NODE) + n = fprintf(of, "%s", Encode(NULL, (char*)np->content)); + + b = CheckDocument(of, np->children); + + if (np->type == XML_ATTRIBUTE_NODE) + n = fprintf(of, "\""); + else if (!b && np->type == XML_ELEMENT_NODE) + n = fprintf(of, "</%s>", np->name); + + b = CheckDocument(of, np->next); + return false; + } // end of CheckDocument + +/******************************************************************/ +/* Convert node or attribute content to latin characters. */ +/******************************************************************/ +int LIBXMLDOC::Decode(xmlChar *cnt, char *buf, int n) + { + const char *txt = (const char *)cnt; + uint dummy_errors; + uint32 len= copy_and_convert(buf, n, &my_charset_utf8mb3_general_ci, txt, + strlen(txt), &my_charset_utf8mb3_general_ci, + &dummy_errors); + buf[len]= '\0'; + return 0; + } // end of Decode + +/******************************************************************/ +/* Convert node or attribute content to latin characters. */ +/******************************************************************/ +xmlChar *LIBXMLDOC::Encode(PGLOBAL g, char *txt) + { + const CHARSET_INFO *ics= &my_charset_utf8mb3_general_ci; + const CHARSET_INFO *ocs= &my_charset_utf8mb3_general_ci; + size_t i = strlen(txt); + size_t o = i * ocs->mbmaxlen / ics->mbmaxlen + 1; + char *buf; + if (g) { + buf = (char*)PlugSubAlloc(g, NULL, o); + } else { + o = 1024; + buf = Buf; + } // endif g + uint dummy_errors; + uint32 len= copy_and_convert(buf, o, ocs, + txt, i, ics, + &dummy_errors); + buf[len]= '\0'; + return BAD_CAST buf; + } // end of Encode +#endif // 0 + +/* ---------------------- class XML2NODE ------------------------ */ + +/******************************************************************/ +/* XML2NODE constructor. */ +/******************************************************************/ +XML2NODE::XML2NODE(PXDOC dp, xmlNodePtr np) : XMLNODE(dp) + { + Docp = ((PXDOC2)dp)->Docp; + Content = NULL; + Nodep = np; + } // end of XML2NODE constructor + +int XML2NODE::GetType(void) + { + if (trace(1)) + htrc("GetType type=%d\n", Nodep->type); + + return Nodep->type; + } // end of GetType + +/******************************************************************/ +/* Return the node class of next sibling of the node. */ +/******************************************************************/ +PXNODE XML2NODE::GetNext(PGLOBAL g) + { + if (trace(1)) + htrc("GetNext\n"); + + if (!Nodep->next) + Next = NULL; + else // if (!Next) + Next = new(g) XML2NODE(Doc, Nodep->next); + + return Next; + } // end of GetNext + +/******************************************************************/ +/* Return the node class of first children of the node. */ +/******************************************************************/ +PXNODE XML2NODE::GetChild(PGLOBAL g) + { + if (trace(1)) + htrc("GetChild\n"); + + if (!Nodep->children) + Children = NULL; + else // if (!Children) + Children = new(g) XML2NODE(Doc, Nodep->children); + + return Children; + } // end of GetChild + +/******************************************************************/ +/* Return the content of a node and subnodes. */ +/******************************************************************/ +RCODE XML2NODE::GetContent(PGLOBAL g, char *buf, int len) + { + RCODE rc = RC_OK; + + if (trace(1)) + htrc("GetContent\n"); + + if (Content) + xmlFree(Content); + + if ((Content = xmlNodeGetContent(Nodep))) { + char *p1 = (char*)Content, *p2 = buf; + bool b = false; + + // Copy content eliminating extra characters + for (; *p1; p1++) + if ((p2 - buf) < len) { + if (strchr(" \t\r\n", *p1)) { + if (b) { + // This to have one blank between sub-nodes + *p2++ = ' '; + b = false; + } // endif b + + } else { + *p2++ = *p1; + b = true; + } // endif p1 + + } else { + snprintf(g->Message, sizeof(g->Message), "Truncated %-.256s content", Nodep->name); + rc = RC_INFO; + } // endif len + + *p2 = 0; + + if (trace(1)) + htrc("GetText buf='%-.256s' len=%d\n", buf, len); + + xmlFree(Content); + Content = NULL; + } else + *buf = '\0'; + + if (trace(1)) + htrc("GetContent: %-.256s\n", buf); + + return rc; + } // end of GetContent + +/******************************************************************/ +/* Set the content of a node. */ +/******************************************************************/ +bool XML2NODE::SetContent(PGLOBAL g, char *txtp, int len) + { + if (trace(1)) + htrc("SetContent: %-.256s\n", txtp); + + xmlChar *buf = xmlEncodeEntitiesReentrant(Docp, BAD_CAST txtp); + + if (trace(1)) + htrc("SetContent: %-.256s -> %-.256s\n", txtp, buf); + + xmlNodeSetContent(Nodep, buf); + xmlFree(buf); + return false; + } // end of SetContent + +/******************************************************************/ +/* Return a clone of this node. */ +/******************************************************************/ +PXNODE XML2NODE::Clone(PGLOBAL g, PXNODE np) + { + if (trace(1)) + htrc("Clone: np=%p\n", np); + + if (np) { + ((PNODE2)np)->Nodep = Nodep; + return np; + } else + return new(g) XML2NODE(Doc, Nodep); + + } // end of Clone + +/******************************************************************/ +/* Return the list of all or matching children that are elements.*/ +/******************************************************************/ +PXLIST XML2NODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp) + { + if (trace(1)) + htrc("GetChildElements: %-.256s\n", xp); + + return SelectNodes(g, (xp) ? xp : (char*)"*", lp); + } // end of GetChildElements + +/******************************************************************/ +/* Return the list of nodes verifying the passed Xpath. */ +/******************************************************************/ +PXLIST XML2NODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp) + { + if (trace(1)) + htrc("SelectNodes: %-.256s\n", xp); + + xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp); + + if (lp) { + ((PLIST2)lp)->Listp = nl; + return lp; + } else + return new(g) XML2NODELIST(Doc, nl); + + } // end of SelectNodes + +/******************************************************************/ +/* Return the first node verifying the passed Xapth. */ +/******************************************************************/ +PXNODE XML2NODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np) + { + if (trace(1)) + htrc("SelectSingleNode: %-.256s\n", xp); + + xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp); + + if (nl && nl->nodeNr) { + if (np) { + ((PNODE2)np)->Nodep = nl->nodeTab[0]; + return np; + } else + return new(g) XML2NODE(Doc, nl->nodeTab[0]); + + } else + return NULL; + + } // end of SelectSingleNode + +/******************************************************************/ +/* Return the node attribute with the specified name. */ +/******************************************************************/ +PXATTR XML2NODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap) + { + xmlAttrPtr atp; + + if (trace(1)) + htrc("GetAttribute: %-.256s\n", SVP(name)); + + if (name) + atp = xmlHasProp(Nodep, BAD_CAST name); + else + atp = Nodep->properties; + + + if (atp) { + if (ap) { + ((PATTR2)ap)->Atrp = atp; + ((PATTR2)ap)->Parent = Nodep; + return ap; + } else + return new(g) XML2ATTR(Doc, atp, Nodep); + + } else + return NULL; + + } // end of GetAttribute + +/******************************************************************/ +/* Add a new child node to this node and return it. */ +/******************************************************************/ +PXNODE XML2NODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np) + { + char *p, *pn, *pf = NULL, *nmp = PlugDup(g, name); + + if (trace(1)) + htrc("AddChildNode: %-.256s\n", name); + + // Is a prefix specified + if ((pn = strchr(nmp, ':'))) { + pf = nmp; + *pn++ = '\0'; // Separate name from prefix + } else + pn = nmp; + + // If name has the format m[n] only m is taken as node name + if ((p = strchr(pn, '['))) + p = BufAlloc(g, pn, int(p - pn)); + else + p = pn; + + xmlNodePtr nop = xmlNewChild(Nodep, NULL, BAD_CAST p, NULL); + + if (!nop) + return NULL; + + if (pf) { + // Prefixed name, is it the default NS prefix? + if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) + pf = NULL; // Default namespace + + xmlNsPtr nsp = xmlSearchNs(Docp, nop, BAD_CAST pf); + + if (!nsp) + nsp = xmlNewNs(nop, NULL, BAD_CAST pf); + + // Set node namespace + nop->ns = nsp; + *(--p) = ':'; // Restore Xname + } else if (Doc->DefNs && xmlSearchNs(Docp, nop, NULL)) + // Not in default namespace + nop->ns = xmlNewNs(nop, BAD_CAST "", NULL); + + if (np) + ((PNODE2)np)->Nodep = nop; + else + np = new(g) XML2NODE(Doc, nop); + + return NewChild(np); + } // end of AddChildNode + +/******************************************************************/ +/* Add a new property to this node and return it. */ +/******************************************************************/ +PXATTR XML2NODE::AddProperty(PGLOBAL g, char *name, PXATTR ap) + { + if (trace(1)) + htrc("AddProperty: %-.256s\n", name); + + xmlAttrPtr atp = xmlNewProp(Nodep, BAD_CAST name, NULL); + + if (atp) { + if (ap) { + ((PATTR2)ap)->Atrp = atp; + ((PATTR2)ap)->Parent = Nodep; + return ap; + } else + return new(g) XML2ATTR(Doc, atp, Nodep); + + } else + return NULL; + + } // end of AddProperty + +/******************************************************************/ +/* Add a new text node to this node. */ +/******************************************************************/ +void XML2NODE::AddText(PGLOBAL g, PCSZ txtp) + { + if (trace(1)) + htrc("AddText: %-.256s\n", txtp); + + // This is to avoid a blank line when inserting a new line + xmlNodePtr np = xmlGetLastChild(Nodep); + + if (np && np->type == XML_TEXT_NODE) { + xmlUnlinkNode(np); + xmlFreeNode(np); + } // endif type + + // Add the new text + xmlAddChild(Nodep, xmlNewText(BAD_CAST txtp)); + } // end of AddText + +/******************************************************************/ +/* Remove a child node from this node. */ +/******************************************************************/ +void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp) + { + xmlErrorPtr xerr; + + if (trace(1)) + htrc("DeleteChild: node=%p\n", dnp); + + xmlNodePtr np = ((PNODE2)dnp)->Nodep; + xmlNodePtr text = np->next; + + // This is specific to row nodes + if (text && text->type == XML_TEXT_NODE) { + xmlUnlinkNode(text); + + if ((xerr = xmlGetLastError())) + goto err; + + xmlFreeNode(text); + + if ((xerr = xmlGetLastError())) + goto err; + + } // endif type + + xmlUnlinkNode(np); + + if ((xerr = xmlGetLastError())) + goto err; + + xmlFreeNode(np); + + if ((xerr = xmlGetLastError())) + goto err; + + Delete(dnp); + + if ((xerr = xmlGetLastError())) + goto err; + + return; + +err: + if (trace(1)) + htrc("DeleteChild: errmsg=%-.256s\n", xerr->message); + + xmlResetError(xerr); + } // end of DeleteChild + +/* -------------------- class XML2NODELIST ---------------------- */ + +/******************************************************************/ +/* XML2NODELIST constructor. */ +/******************************************************************/ +XML2NODELIST::XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp) + : XMLNODELIST(dp) + { + Listp = lp; + } // end of XML2NODELIST constructor + +/******************************************************************/ +/* Return the length of the list. */ +/******************************************************************/ +int XML2NODELIST::GetLength(void) + { + return (Listp) ? Listp->nodeNr : 0; + } // end of GetLength + +/******************************************************************/ +/* Return the nth element of the list. */ +/******************************************************************/ +PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np) + { + if (trace(1)) + htrc("GetItem: %d\n", n); + + if (!Listp || Listp->nodeNr <= n) + return NULL; + + if (np) { + ((PNODE2)np)->Nodep = Listp->nodeTab[n]; + return np; + } else + return new(g) XML2NODE(Doc, Listp->nodeTab[n]); + + } // end of GetItem + +/******************************************************************/ +/* Reset the pointer on the deleted item. */ +/******************************************************************/ +bool XML2NODELIST::DropItem(PGLOBAL g, int n) + { + if (trace(1)) + htrc("DropItem: n=%d\n", n); + + // We should do something here + if (!Listp || Listp->nodeNr <= n) + return true; + + Listp->nodeTab[n] = NULL; // This was causing Valgrind warning + return false; + } // end of DropItem + +/* ---------------------- class XML2ATTR ------------------------ */ + +/******************************************************************/ +/* XML2ATTR constructor. */ +/******************************************************************/ +XML2ATTR::XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np) + : XMLATTRIBUTE(dp) + { + Atrp = ap; + Parent = np; + } // end of XML2ATTR constructor + +/******************************************************************/ +/* Return the next sibling of the attribute. */ +/******************************************************************/ +PXATTR XML2ATTR::GetNext(PGLOBAL g) + { + if (trace(1)) + htrc("Attr GetNext\n"); + + if (!Atrp->next) + return NULL; + else + return new(g) XML2ATTR(Doc, Atrp->next, Atrp->parent); + + } // end of GetNext + +/******************************************************************/ +/* Return the text of an attribute. */ +/******************************************************************/ +RCODE XML2ATTR::GetText(PGLOBAL g, char *buf, int len) + { + RCODE rc = RC_OK; + xmlChar *txt; + + if (trace(1)) + htrc("GetText\n"); + + if ((txt = xmlGetProp(Atrp->parent, Atrp->name))) { + // Copy the text to the buffer + if (strlen((char*)txt) >= (unsigned)len) { + memcpy(buf, txt, len - 1); + buf[len - 1] = 0; + snprintf(g->Message, sizeof(g->Message), "Truncated %-.256s content", Atrp->name); + rc = RC_INFO; + } else + strcpy(buf, (const char*)txt); + + xmlFree(txt); + } else + *buf = '\0'; + + if (trace(1)) + htrc("GetText: %-.256s\n", buf); + + return rc; + } // end of GetText + +/******************************************************************/ +/* Set the content of an attribute. */ +/******************************************************************/ +bool XML2ATTR::SetText(PGLOBAL g, char *txtp, int len) + { + if (trace(1)) + htrc("SetText: %-.256s %d\n", txtp, len); + + xmlSetProp(Parent, Atrp->name, BAD_CAST txtp); + return false; + } // end of SetText |