// $Id$ /************************************************************ * The Data Acquisition Backbone Core (DABC) * ************************************************************ * Copyright (C) 2009 - * * GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Planckstr. 1, 64291 Darmstadt, Germany * * Contact: http://dabc.gsi.de * ************************************************************ * This software can be used under the GPL license * * agreements as stated in LICENSE.txt file * * which is part of the distribution. * ************************************************************/ #include "dabc/XmlEngine.h" #include #include #include #include "dabc/logging.h" namespace dabc { struct SXmlAttr_t { SXmlAttr_t *fNext; // after structure itself memory for attribute name is preserved // if first byte is 0, this is special attribute static inline char* Name(void* arg) { return (char*)arg + sizeof(SXmlAttr_t); } }; enum EXmlNodeType { kXML_NODE = 1, // normal node with childs kXML_COMMENT = 2, // comment (stored as value of node fName) kXML_PI_NODE = 3, // processing instructions node (like kXML_RAWLINE = 4 // just one line of xml code }; struct SXmlNode_t { EXmlNodeType fType; // this is node type - node, comment, processing instruction and so on SXmlAttr_t *fAttr; // first attribute SXmlAttr_t *fNs; // name space definition (if any) SXmlNode_t *fNext; // next node on the same level of hierarchy SXmlNode_t *fChild; // first child node SXmlNode_t *fLastChild; // last child node SXmlNode_t *fParent; // parent node // consequent bytes after structure are node name // if first byte is 0, next is node content static inline char* Name(void* arg) { return (char*)arg + sizeof(SXmlNode_t); } }; struct SXmlDoc_t { SXmlNode_t *fRootNode; char *fDtdName; char *fDtdRoot; }; class XmlOutputStream { protected: std::ostream *fOut{nullptr}; std::string *fOutStr{nullptr}; char *fBuf{nullptr}; char *fCurrent{nullptr}; char *fMaxAddr{nullptr}; char *fLimitAddr{nullptr}; public: XmlOutputStream(const char *filename, int bufsize = 20000) { fOut = new std::ofstream(filename); fOutStr = nullptr; Init(bufsize); } XmlOutputStream(std::string* outstr, int bufsize = 20000) { fOut = nullptr; fOutStr = outstr; Init(bufsize); } void Init(int bufsize) { fBuf = (char *) std::malloc(bufsize); fCurrent = fBuf; fMaxAddr = fBuf + bufsize; fLimitAddr = fBuf + int(bufsize*0.75); } virtual ~XmlOutputStream() { if (fCurrent!=fBuf) OutputCurrent(); delete fOut; fOut = nullptr; std::free(fBuf); fBuf = nullptr; } void OutputCurrent() { if (fCurrent!=fBuf) { if (fOut) fOut->write(fBuf, fCurrent-fBuf); else if (fOutStr) fOutStr->append(fBuf, fCurrent-fBuf); } fCurrent = fBuf; } void OutputChar(char symb) { if (fOut) fOut->put(symb); else if (fOutStr) fOutStr->append(1, symb); } void Write(const char *str) { int len = strlen(str); if (fCurrent+len>=fMaxAddr) { OutputCurrent(); fOut->write(str,len); } else { while (*str) *fCurrent++ = *str++; if (fCurrent>fLimitAddr) OutputCurrent(); } } void Put(char symb, int cnt=1) { if (fCurrent+cnt>=fMaxAddr) OutputCurrent(); if (fCurrent+cnt>=fMaxAddr) for(int n=0;nfLimitAddr) OutputCurrent(); } } }; class XmlInputStream { protected: std::istream *fInp{nullptr}; const char *fInpStr{nullptr}; int fInpStrLen{0}; char *fBuf{nullptr}; int fBufSize{0}; char *fMaxAddr{nullptr}; char *fLimitAddr{nullptr}; int fTotalPos{0}; int fCurrentLine{0}; public: char *fCurrent{nullptr}; XmlInputStream(bool isfilename, const char *filename, int ibufsize) { if (isfilename) { fInp = new std::ifstream(filename); fInpStr = nullptr; fInpStrLen = 0; } else { fInp = nullptr; fInpStr = filename; fInpStrLen = !filename ? 0 : strlen(filename); } fBufSize = ibufsize; fBuf = (char *) std::malloc(fBufSize); fCurrent = nullptr; fMaxAddr = nullptr; int len = DoRead(fBuf, fBufSize); fCurrent = fBuf; fMaxAddr = fBuf+len; fLimitAddr = fBuf + int(len*0.75); fTotalPos = 0; fCurrentLine = 1; } virtual ~XmlInputStream() { delete fInp; fInp = nullptr; std::free(fBuf); fBuf = nullptr; } inline bool IsBad() { return fInp ? !(*fInp) : (fInpStr == nullptr); } inline bool SkipComments() const { return false; } inline bool EndOfFile() { return fInp ? fInp->eof() : (fInpStrLen <= 0); } inline bool EndOfStream() { return EndOfFile() && (fCurrent>=fMaxAddr); } #if defined(__GNUC__) && (__GNUC__ >= 7) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif int DoRead(char* buf, int maxsize) { if (EndOfFile()) return 0; if (fInp) { fInp->get(buf, maxsize, 0); maxsize = strlen(buf); } else { if (maxsize > fInpStrLen) maxsize = fInpStrLen; if (maxsize > 0) strncpy(buf, fInpStr, maxsize); fInpStr+=maxsize; fInpStrLen-=maxsize; } return maxsize; } #if defined(__GNUC__) && (__GNUC__ >= 7) #pragma GCC diagnostic pop #endif bool ExpandStream(char *&curr) { if (EndOfFile()) return false; fBufSize*=2; int curlength = fMaxAddr - fBuf; char* newbuf = (char*) realloc(fBuf, fBufSize); if (!newbuf) return false; fMaxAddr = newbuf + (fMaxAddr - fBuf); fCurrent = newbuf + (fCurrent - fBuf); fLimitAddr = newbuf + (fLimitAddr - fBuf); curr = newbuf + (curr - fBuf); fBuf = newbuf; int len = DoRead(fMaxAddr, fBufSize-curlength); if (len == 0) return false; fMaxAddr += len; fLimitAddr += int(len*0.75); return true; } bool ShiftStream() { if (fCurrent= fMaxAddr) EOUT("Error remains???"); if (*fCurrent == 10) fCurrentLine++; if (fCurrent >= fLimitAddr) { ShiftStream(); if (fCurrent >= fMaxAddr) return false; } fCurrent++; } fTotalPos += sz; return true; } bool SkipSpaces(bool tillendl = false) { while (fCurrent < fMaxAddr) { char symb = *fCurrent; if ((symb > 26) && (symb != ' ')) return true; if (!ShiftCurrent()) return false; if (tillendl && (symb == 10)) return true; } return false; } bool CheckFor(const char *str) { // Check if in current position we see specified string int len = strlen(str); char* curr = fCurrent; while (curr+len>fMaxAddr) if (!ExpandStream(curr)) return false; while (*str != 0) if (*str++ != *curr++) return false; return ShiftCurrent(len); } int SearchFor(const char *str) { // Search for specified string in the stream // return number of symbols before string was found, -1 if error int len = strlen(str); char* curr = fCurrent; while(true) { while (curr+len > fMaxAddr) if (!ExpandStream(curr)) return -1; const char *chk0 = curr; const char *chk = str; bool find = true; while (*chk != 0) if (*chk++ != *chk0++) { find = false; break; } // if string found, shift to the next symbol after string if (find) return curr - fCurrent; curr++; } return -1; } int LocateIdentifier() { char symb = *fCurrent; bool ok = (((symb>='a') && (symb<='z')) || ((symb>='A') && (symb<='Z')) || (symb=='_')); if (!ok) return 0; char* curr = fCurrent; do { curr++; if (curr>=fMaxAddr) if (!ExpandStream(curr)) return 0; symb = *curr; ok = ((symb>='a') && (symb<='z')) || ((symb>='A') && (symb<='Z')) || ((symb>='0') && (symb<='9')) || (symb==':') || (symb=='_') || (symb=='-') || (symb=='.'); if (!ok) return curr-fCurrent; } while (curr=fMaxAddr) if (!ExpandStream(curr)) return -1; } return -1; } int LocateAttributeValue(char* start) { char* curr = start; if (curr>=fMaxAddr) if (!ExpandStream(curr)) return 0; if (*curr!='=') return 0; curr++; if (curr>=fMaxAddr) if (!ExpandStream(curr)) return 0; if (*curr!='"') return 0; do { curr++; if (curr>=fMaxAddr) if (!ExpandStream(curr)) return 0; if (*curr=='"') return curr-start+1; } while (currfAttr; while (attr) { if (strcmp(SXmlAttr_t::Name(attr),name) == 0) return true; attr = attr->fNext; } return false; } //______________________________________________________________________________ const char *dabc::Xml::GetAttr(XMLNodePointer_t xmlnode, const char *name) { // returns value of attribute for xmlnode if (!xmlnode) return nullptr; SXmlAttr_t* attr = ((SXmlNode_t*)xmlnode)->fAttr; while (attr) { if (strcmp(SXmlAttr_t::Name(attr),name) == 0) return SXmlAttr_t::Name(attr) + strlen(name) + 1; attr = attr->fNext; } return nullptr; } //______________________________________________________________________________ int dabc::Xml::GetIntAttr(XMLNodePointer_t xmlnode, const char *name) { // returns value of attribute as integer if (!xmlnode) return 0; int res = 0; const char *attr = GetAttr(xmlnode, name); if (attr) sscanf(attr, "%d", &res); return res; } //______________________________________________________________________________ dabc::XMLAttrPointer_t dabc::Xml::NewAttr(XMLNodePointer_t xmlnode, XMLNsPointer_t, const char *name, const char *value) { // creates new attribute for xmlnode, // namespaces are not supported for attributes if (!xmlnode) return nullptr; int namelen = name ? strlen(name) : 0; int valuelen = value ? strlen(value) : 0; SXmlAttr_t* attr = (SXmlAttr_t*) AllocateAttr(namelen, valuelen, xmlnode); char* attrname = SXmlAttr_t::Name(attr); if (namelen>0) strncpy(attrname, name, namelen+1); else *attrname = 0; attrname += (namelen + 1); if (valuelen>0) strncpy(attrname, value, valuelen+1); else *attrname = 0; return (XMLAttrPointer_t) attr; } //______________________________________________________________________________ dabc::XMLAttrPointer_t dabc::Xml::NewIntAttr(XMLNodePointer_t xmlnode, const char *name, int value) { // create node attribute with integer value char sbuf[30]; snprintf(sbuf, sizeof(sbuf), "%d", value); return NewAttr(xmlnode, nullptr, name, sbuf); } //______________________________________________________________________________ void dabc::Xml::FreeAttr(XMLNodePointer_t xmlnode, const char *name) { // remove attribute from xmlnode if (!xmlnode) return; SXmlAttr_t* attr = ((SXmlNode_t*) xmlnode)->fAttr; SXmlAttr_t* prev = nullptr; while (attr) { if (strcmp(SXmlAttr_t::Name(attr),name) == 0) { if (prev) prev->fNext = attr->fNext; else ((SXmlNode_t*) xmlnode)->fAttr = attr->fNext; //fNumNodes--; std::free(attr); return; } prev = attr; attr = attr->fNext; } } //______________________________________________________________________________ void dabc::Xml::FreeAllAttr(XMLNodePointer_t xmlnode) { // Free all attributes of the node if (!xmlnode) return; SXmlNode_t* node = (SXmlNode_t*) xmlnode; SXmlAttr_t* attr = node->fAttr; while (attr) { SXmlAttr_t* next = attr->fNext; std::free(attr); attr = next; } node->fAttr = nullptr; } //______________________________________________________________________________ dabc::XMLAttrPointer_t dabc::Xml::GetFirstAttr(XMLNodePointer_t xmlnode) { // return first attribute in the list, namespace (if exists) will be skipped if (!xmlnode) return nullptr; SXmlNode_t* node = (SXmlNode_t*) xmlnode; SXmlAttr_t* attr = node->fAttr; if (attr && (node->fNs == attr)) attr = attr->fNext; return (XMLAttrPointer_t) attr; } //______________________________________________________________________________ dabc::XMLAttrPointer_t dabc::Xml::GetNextAttr(XMLAttrPointer_t xmlattr) { // return next attribute in the list if (!xmlattr) return nullptr; return (XMLAttrPointer_t) ((SXmlAttr_t*) xmlattr)->fNext; } //______________________________________________________________________________ const char *dabc::Xml::GetAttrName(XMLAttrPointer_t xmlattr) { // return name of the attribute if (!xmlattr) return nullptr; return SXmlAttr_t::Name(xmlattr); } //______________________________________________________________________________ const char *dabc::Xml::GetAttrValue(XMLAttrPointer_t xmlattr) { // return value of attribute if (!xmlattr) return nullptr; const char *attrname = SXmlAttr_t::Name(xmlattr); return attrname + strlen(attrname) + 1; } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::NewChild(XMLNodePointer_t parent, XMLNsPointer_t ns, const char *name, const char *content) { // create new child element for parent node int namelen = name ? strlen(name) : 0; SXmlNode_t* node = (SXmlNode_t*) AllocateNode(namelen, parent); if (namelen > 0) strncpy(SXmlNode_t::Name(node), name, namelen+1); else *SXmlNode_t::Name(node) = 0; node->fNs = (SXmlAttr_t*) ns; if (content) { int contlen = strlen(content); if (contlen>0) { SXmlNode_t* contnode = (SXmlNode_t*) AllocateNode(contlen+1, node); char* cptr = SXmlNode_t::Name(contnode); // first zero indicate that this is just content value *cptr = 0; cptr++; strncpy(cptr, content, contlen+1); } } return (XMLNodePointer_t) node; } //______________________________________________________________________________ dabc::XMLNsPointer_t dabc::Xml::NewNS(XMLNodePointer_t xmlnode, const char *reference, const char *name) { // create namespace attribute for xmlnode. // namespace attribute will be always the first in list of node attributes SXmlNode_t* node = (SXmlNode_t*) xmlnode; if (!name) name = SXmlNode_t::Name(node); int namelen = strlen(name); char* nsname = new char[namelen+7]; snprintf(nsname, namelen+7, "xmlns:%s", name); SXmlAttr_t* first = node->fAttr; node->fAttr = nullptr; SXmlAttr_t* nsattr = (SXmlAttr_t*) NewAttr(xmlnode, nullptr, nsname, reference); node->fAttr = nsattr; nsattr->fNext = first; node->fNs = nsattr; delete[] nsname; return (XMLNsPointer_t) nsattr; } //______________________________________________________________________________ dabc::XMLNsPointer_t dabc::Xml::GetNS(XMLNodePointer_t xmlnode) { // return namespace attribute (if exists) if (!xmlnode) return nullptr; SXmlNode_t* node = (SXmlNode_t*) xmlnode; return (XMLNsPointer_t) node->fNs; } //______________________________________________________________________________ const char *dabc::Xml::GetNSName(XMLNsPointer_t ns) { // return name id of namespace const char *nsname = GetAttrName((XMLAttrPointer_t)ns); if (nsname && (strncmp(nsname,"xmlns:",6) == 0)) nsname += 6; return nsname; } //______________________________________________________________________________ const char *dabc::Xml::GetNSReference(XMLNsPointer_t ns) { // return reference id of namespace return GetAttrValue((XMLAttrPointer_t)ns); } //______________________________________________________________________________ void dabc::Xml::AddChild(XMLNodePointer_t parent, XMLNodePointer_t child) { // add child element to xmlnode if (!parent || !child) return; SXmlNode_t* pnode = (SXmlNode_t*) parent; SXmlNode_t* cnode = (SXmlNode_t*) child; cnode->fParent = pnode; if (!pnode->fLastChild) { pnode->fChild = cnode; pnode->fLastChild = cnode; } else { //SXmlNode_t* ch = pnode->fChild; //while (ch->fNext) ch = ch->fNext; pnode->fLastChild->fNext = cnode; pnode->fLastChild = cnode; } } //______________________________________________________________________________ void dabc::Xml::AddChildFirst(XMLNodePointer_t parent, XMLNodePointer_t child) { // add node as first child if (!parent || !child) return; SXmlNode_t* pnode = (SXmlNode_t*) parent; SXmlNode_t* cnode = (SXmlNode_t*) child; cnode->fParent = pnode; cnode->fNext = pnode->fChild; pnode->fChild = cnode; if (pnode->fLastChild == nullptr) pnode->fLastChild = cnode; } //______________________________________________________________________________ bool dabc::Xml::AddComment(XMLNodePointer_t xmlnode, const char *comment) { // Adds comment line to the node if (!xmlnode || !comment) return false; int commentlen = strlen(comment); SXmlNode_t* node = (SXmlNode_t*) AllocateNode(commentlen, xmlnode); node->fType = kXML_COMMENT; strncpy(SXmlNode_t::Name(node), comment, commentlen+1); return true; } //______________________________________________________________________________ bool dabc::Xml::AddDocComment(XMLDocPointer_t xmldoc, const char *comment) { // add comment line to the top of the document if (!xmldoc) return false; XMLNodePointer_t rootnode = DocGetRootElement(xmldoc); UnlinkNode(rootnode); bool res = AddComment(((SXmlDoc_t*)xmldoc)->fRootNode, comment); AddChild((XMLNodePointer_t) ((SXmlDoc_t*)xmldoc)->fRootNode, rootnode); return res; } //______________________________________________________________________________ bool dabc::Xml::AddRawLine(XMLNodePointer_t xmlnode, const char *line) { // Add just line into xml file // Line should has correct xml syntax that later it can be decoded by xml parser // For instance, it can be comment or processing instructions if (!xmlnode || !line) return false; int linelen = strlen(line); SXmlNode_t* node = (SXmlNode_t*) AllocateNode(linelen, xmlnode); node->fType = kXML_RAWLINE; strncpy(SXmlNode_t::Name(node), line, linelen+1); return true; } //______________________________________________________________________________ bool dabc::Xml::AddDocRawLine(XMLDocPointer_t xmldoc, const char *line) { // Add just line on the top of xml document // Line should has correct xml syntax that later it can be decoded by xml parser XMLNodePointer_t rootnode = DocGetRootElement(xmldoc); UnlinkNode(rootnode); bool res = AddRawLine(((SXmlDoc_t*)xmldoc)->fRootNode, line); AddChild((XMLNodePointer_t) ((SXmlDoc_t*)xmldoc)->fRootNode, rootnode); return res; } //______________________________________________________________________________ bool dabc::Xml::AddStyleSheet(XMLNodePointer_t xmlnode, const char *href, const char *type, const char *title, int alternate, const char *media, const char *charset) { // Adds style sheet definition to the specified node // Creates // Attributes href and type must be supplied, // other attributes: title, alternate, media, charset are optional // if alternate = 0, attribute alternate="no" will be created, // if alternate > 0, attribute alternate="yes" // if alternate < 0, attribute will not be created if (!xmlnode || !href || !type) return false; const char *nodename = "xml-stylesheet"; int nodenamelen = strlen(nodename); SXmlNode_t* node = (SXmlNode_t*) AllocateNode(nodenamelen, xmlnode); node->fType = kXML_PI_NODE; strncpy(SXmlNode_t::Name(node), nodename, nodenamelen+1); if (alternate >= 0) NewAttr(node, nullptr, "alternate", (alternate > 0) ? "yes" : "no"); if (title) NewAttr(node, nullptr, "title", title); NewAttr(node, nullptr, "href", href); NewAttr(node, nullptr, "type", type); if (media) NewAttr(node, nullptr, "media", media); if (charset) NewAttr(node, nullptr, "charset", charset); return true; } //______________________________________________________________________________ bool dabc::Xml::AddDocStyleSheet(XMLDocPointer_t xmldoc, const char *href, const char *type, const char *title, int alternate, const char *media, const char *charset) { // Add style sheet definition on the top of document if (!xmldoc) return false; XMLNodePointer_t rootnode = DocGetRootElement(xmldoc); UnlinkNode(rootnode); bool res = AddStyleSheet(((SXmlDoc_t*)xmldoc)->fRootNode, href,type,title,alternate,media,charset); AddChild((XMLNodePointer_t) ((SXmlDoc_t*)xmldoc)->fRootNode, rootnode); return res; } //______________________________________________________________________________ void dabc::Xml::UnlinkNode(XMLNodePointer_t xmlnode) { // unlink (detach) xml node from parent if (!xmlnode) return; SXmlNode_t* node = (SXmlNode_t*) xmlnode; SXmlNode_t* parent = node->fParent; if (!parent) return; if (parent->fChild==node) { parent->fChild = node->fNext; if (parent->fLastChild==node) parent->fLastChild = node->fNext; } else { SXmlNode_t* ch = parent->fChild; while (ch->fNext!=node) ch = ch->fNext; ch->fNext = node->fNext; if (parent->fLastChild == node) parent->fLastChild = ch; } } //______________________________________________________________________________ void dabc::Xml::FreeNode(XMLNodePointer_t xmlnode) { // release all memory, allocated from this node and // destroyes node itself if (!xmlnode) return; SXmlNode_t* node = (SXmlNode_t*) xmlnode; SXmlNode_t* child = node->fChild; while (child) { SXmlNode_t* next = child->fNext; FreeNode((XMLNodePointer_t)child); child = next; } SXmlAttr_t* attr = node->fAttr; while (attr) { SXmlAttr_t* next = attr->fNext; //fNumNodes--; std::free(attr); attr = next; } std::free(node); //fNumNodes--; } //______________________________________________________________________________ void dabc::Xml::UnlinkFreeNode(XMLNodePointer_t xmlnode) { // combined operation. Unlink node and free used memory UnlinkNode(xmlnode); FreeNode(xmlnode); } //______________________________________________________________________________ const char *dabc::Xml::GetNodeName(XMLNodePointer_t xmlnode) { // returns name of xmlnode return !xmlnode ? nullptr : SXmlNode_t::Name(xmlnode); } //______________________________________________________________________________ const char *dabc::Xml::GetNodeContent(XMLNodePointer_t xmlnode) { // get contents (if any) of xml node if (!xmlnode) return nullptr; SXmlNode_t* node = (SXmlNode_t*) xmlnode; if (!node->fChild) return nullptr; const char *childname = SXmlNode_t::Name(node->fChild); if (!childname || (*childname != 0)) return nullptr; return childname + 1; } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::GetChild(XMLNodePointer_t xmlnode) { // returns first child of xml node SXmlNode_t* res = !xmlnode ? nullptr :((SXmlNode_t*) xmlnode)->fChild; // skip content node if (res && (*SXmlNode_t::Name(res) == 0)) res = res->fNext; return (XMLNodePointer_t) res; } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::GetParent(XMLNodePointer_t xmlnode) { // returns parent of xmlnode return !xmlnode ? nullptr : (XMLNodePointer_t) ((SXmlNode_t*) xmlnode)->fParent; } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::GetNext(XMLNodePointer_t xmlnode) { // return next to xmlnode node return !xmlnode ? nullptr : (XMLNodePointer_t) ((SXmlNode_t*) xmlnode)->fNext; } //______________________________________________________________________________ void dabc::Xml::ShiftToNext(XMLNodePointer_t &xmlnode, bool tonode) { // shifts specified node to next do { xmlnode = !xmlnode ? nullptr : (XMLNodePointer_t) ((SXmlNode_t*) xmlnode)->fNext; if (!xmlnode || !tonode) return; } while (((SXmlNode_t*) xmlnode)->fType != kXML_NODE); } //______________________________________________________________________________ bool dabc::Xml::IsEmptyNode(XMLNodePointer_t xmlnode) { // return true is this is node with special data like comments to data processing instructions return !xmlnode ? true : (((SXmlNode_t*) xmlnode)->fType != kXML_NODE); } //______________________________________________________________________________ void dabc::Xml::SkipEmpty(XMLNodePointer_t &xmlnode) { // Skip all current empty nodes and locate on first "true" node if (IsEmptyNode(xmlnode)) ShiftToNext(xmlnode); } //______________________________________________________________________________ void dabc::Xml::CleanNode(XMLNodePointer_t xmlnode) { // remove all childs node from xmlnode if (!xmlnode) return; SXmlNode_t* node = (SXmlNode_t*) xmlnode; SXmlNode_t* child = node->fChild; while (child) { SXmlNode_t* next = child->fNext; FreeNode((XMLNodePointer_t)child); child = next; } node->fChild = nullptr; node->fLastChild = nullptr; } //______________________________________________________________________________ dabc::XMLDocPointer_t dabc::Xml::NewDoc(const char *version) { // creates new xml document with provided version SXmlDoc_t* doc = new SXmlDoc_t; doc->fRootNode = (SXmlNode_t*) NewChild(nullptr, nullptr, "??DummyTopNode??", nullptr); if (version) { XMLNodePointer_t vernode = NewChild( (XMLNodePointer_t) doc->fRootNode, nullptr, "xml"); ((SXmlNode_t*) vernode)->fType = kXML_PI_NODE; NewAttr(vernode, nullptr, "version", version); } doc->fDtdName = nullptr; doc->fDtdRoot = nullptr; return (XMLDocPointer_t) doc; } //______________________________________________________________________________ void dabc::Xml::AssignDtd(XMLDocPointer_t xmldoc, const char *dtdname, const char *rootname) { // assignes dtd filename to document if (!xmldoc) return; SXmlDoc_t* doc = (SXmlDoc_t*) xmldoc; delete[] doc->fDtdName; doc->fDtdName = Makestr(dtdname); delete[] doc->fDtdRoot; doc->fDtdRoot = Makestr(rootname); } //______________________________________________________________________________ void dabc::Xml::FreeDoc(XMLDocPointer_t xmldoc) { // frees allocated document data and deletes document itself if (!xmldoc) return; SXmlDoc_t* doc = (SXmlDoc_t*) xmldoc; FreeNode((XMLNodePointer_t) doc->fRootNode); delete[] doc->fDtdName; delete[] doc->fDtdRoot; delete doc; } //______________________________________________________________________________ void dabc::Xml::SaveDoc(XMLDocPointer_t xmldoc, const char *filename, int layout) { // store document content to file // if layout<=0, no any spaces or newlines will be placed between // xmlnodes. Xml file will have minimum size, but nonreadable structure // if (layout>0) each node will be started from new line, // and number of spaces will correspond to structure depth. if (!xmldoc) return; SXmlDoc_t* doc = (SXmlDoc_t*) xmldoc; XmlOutputStream out(filename, 100000); XMLNodePointer_t child = GetChild((XMLNodePointer_t) doc->fRootNode); do { SaveNode(child, &out, layout, 0); ShiftToNext(child, false); } while (child); } //______________________________________________________________________________ void dabc::Xml::DocSetRootElement(XMLDocPointer_t xmldoc, XMLNodePointer_t xmlnode) { // set main (root) node for document if (!xmldoc) return; FreeNode(DocGetRootElement(xmldoc)); AddChild((XMLNodePointer_t) ((SXmlDoc_t*)xmldoc)->fRootNode, xmlnode); } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::DocGetRootElement(XMLDocPointer_t xmldoc) { // returns root node of document if (!xmldoc) return nullptr; XMLNodePointer_t xmlnode = (XMLNodePointer_t) ((SXmlDoc_t*)xmldoc)->fRootNode; xmlnode = GetChild(xmlnode); ShiftToNext(xmlnode); return xmlnode; } //______________________________________________________________________________ dabc::XMLDocPointer_t dabc::Xml::ParseFile(const char *filename, bool showerr) { // parses content of file and tries to produce xml structures if (!filename || (strlen(filename) == 0)) return nullptr; XmlInputStream inp(true, filename, 100000); if (inp.IsBad()) { EOUT("File %s not found", filename); return nullptr; } return ParseStream(&inp, showerr); } //______________________________________________________________________________ dabc::XMLDocPointer_t dabc::Xml::ParseString(const char *xmlstring, bool showerr) { // parses content of string and tries to produce xml structures if (!xmlstring || (strlen(xmlstring) == 0)) return nullptr; XmlInputStream inp(false, xmlstring, 100000); return ParseStream(&inp, showerr); } //______________________________________________________________________________ dabc::XMLDocPointer_t dabc::Xml::ParseStream(XmlInputStream* inp, bool showerr) { // parses content of the stream and tries to produce xml structures if (!inp) return nullptr; XMLDocPointer_t xmldoc = NewDoc(nullptr); bool success = false; int resvalue = 0; do { ReadNode(((SXmlDoc_t*) xmldoc)->fRootNode, inp, resvalue); if (resvalue!=2) break; // coverity[unchecked_value] at this place result of SkipSpaces() doesn't matter - either file is finished (false) or there is some more nodes to analyse (true) if (!inp->EndOfStream()) inp->SkipSpaces(); if (inp->EndOfStream()) { success = true; break; } } while (true); if (!success) { if (showerr) DisplayError(resvalue, inp->CurrentLine()); FreeDoc(xmldoc); return nullptr; } return xmldoc; } //______________________________________________________________________________ bool dabc::Xml::ValidateVersion(XMLDocPointer_t xmldoc, const char *version) { // check that first node is xml processing instruction with correct xml version number if (!xmldoc) return false; XMLNodePointer_t vernode = GetChild((XMLNodePointer_t) ((SXmlDoc_t*) xmldoc)->fRootNode); if (!vernode) return false; if (((SXmlNode_t*) vernode)->fType!=kXML_PI_NODE) return false; if (strcmp(GetNodeName(vernode), "xml") != 0) return false; const char *value = GetAttr(vernode,"version"); if (!value) return false; if (!version) version = "1.0"; return strcmp(version,value) == 0; } //______________________________________________________________________________ void dabc::Xml::SaveSingleNode(XMLNodePointer_t xmlnode, std::string* res, int layout) { // convert single xml node (and its child node) to string // if layout<=0, no any spaces or newlines will be placed between // xmlnodes. Xml file will have minimum size, but nonreadable structure // if (layout>0) each node will be started from new line, // and number of spaces will correspond to structure depth. if (!res || !xmlnode) return; XmlOutputStream out(res, 10000); SaveNode(xmlnode, &out, layout, 0); } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::ReadSingleNode(const char *src) { // read single xml node from provided string if (!src) return nullptr; XmlInputStream inp(false, src, 10000); int resvalue; XMLNodePointer_t xmlnode = ReadNode(nullptr, &inp, resvalue); if (resvalue <= 0) { DisplayError(resvalue, inp.CurrentLine()); FreeNode(xmlnode); return nullptr; } return xmlnode; } //______________________________________________________________________________ char* dabc::Xml::Makestr(const char *str) { // creates char* variable with copy of provided string if (!str) return nullptr; int len = strlen(str); if (len == 0) return nullptr; char* res = new char[len+1]; strncpy(res, str, len+1); return res; } //______________________________________________________________________________ char* dabc::Xml::Makenstr(const char *str, int len) { // creates char* variable with copy of len symbols from provided string if (!str || (len == 0)) return nullptr; char* res = new char[len+1]; strncpy(res, str, len); *(res+len) = 0; return res; } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::AllocateNode(int namelen, XMLNodePointer_t parent) { // Allocates new xml node with specified namelength //fNumNodes++; SXmlNode_t* node = (SXmlNode_t*) std::malloc(sizeof(SXmlNode_t) + namelen + 1); if (!node) return nullptr; node->fType = kXML_NODE; node->fParent = nullptr; node->fNs = nullptr; node->fAttr = nullptr; node->fChild = nullptr; node->fLastChild = nullptr; node->fNext = nullptr; if (parent) AddChild(parent, (XMLNodePointer_t) node); return (XMLNodePointer_t) node; } //______________________________________________________________________________ dabc::XMLAttrPointer_t dabc::Xml::AllocateAttr(int namelen, int valuelen, XMLNodePointer_t xmlnode) { // Allocate new attribute with specified name length and value length //fNumNodes++; SXmlAttr_t* attr = (SXmlAttr_t*) std::malloc(sizeof(SXmlAttr_t) + namelen + 1 + valuelen + 1); if (!attr) return nullptr; SXmlNode_t* node = (SXmlNode_t*) xmlnode; attr->fNext = nullptr; if (!node->fAttr) { node->fAttr = attr; } else { SXmlAttr_t* d = node->fAttr; while (d->fNext) d = d->fNext; d->fNext = attr; } return (XMLAttrPointer_t) attr; } //______________________________________________________________________________ dabc::XMLNsPointer_t dabc::Xml::FindNs(XMLNodePointer_t xmlnode, const char *name) { // define if namespace of that name exists for xmlnode SXmlNode_t* node = (SXmlNode_t*) xmlnode; while (node) { if (node->fNs) { const char *nsname = SXmlAttr_t::Name(node->fNs) + 6; if (strcmp(nsname, name) == 0) return node->fNs; } node = node->fParent; } return nullptr; } //______________________________________________________________________________ void dabc::Xml::TruncateNsExtension(XMLNodePointer_t xmlnode) { // removes namespace extension of nodename SXmlNode_t* node = (SXmlNode_t*) xmlnode; if (!node) return; char* colon = strchr(SXmlNode_t::Name(node),':'); if (!colon) return; char* copyname = SXmlNode_t::Name(node); while (*colon) *(copyname++) = *(++colon); } //______________________________________________________________________________ void dabc::Xml::UnpackSpecialCharacters(char* target, const char *source, int srclen) { // unpack special symbols, used in xml syntax to code characters // these symbols: '<' - <, '>' - >, '&' - &, '"' - ", ' - ' while (srclen>0) { if (*source=='&') { if ((srclen>3) && (*(source+1)=='l') && (*(source+2)=='t') && (*(source+3)==';')) { *target++ = '<'; source+=4; srclen-=4; } else if ((srclen>3) && (*(source+1)=='g') && (*(source+2)=='t') && (*(source+3)==';')) { *target++ = '>'; source+=4; srclen-=4; } else if ((srclen>4) && (*(source+1)=='a') && (*(source+2)=='m') && (*(source+3)=='p') && (*(source+4)==';')) { *target++ = '&'; source+=5; srclen-=5; } else if ((srclen>5) && (*(source+1)=='q') && (*(source+2)=='u') && (*(source+3)=='o') && (*(source+4)=='t') && (*(source+5)==';')) { *target++ = '\"'; source+=6; srclen-=6; } else if ((srclen>5) && (*(source+1)=='a') && (*(source+2)=='p') && (*(source+3)=='o') && (*(source+4)=='s') && (*(source+5)==';')) { *target++ = '\''; source+=6; srclen-=6; } else { *target++ = *source++; srclen--; } } else { *target++ = *source++; srclen--; } } *target = 0; } //______________________________________________________________________________ void dabc::Xml::OutputValue(char* value, XmlOutputStream* out) { // output value to output stream // if symbols '<' '&' '>' '"' appears in the string, they // will be encoded to appropriate xml symbols: <, &, >, " if (!value) return; char* last = value; char* find = nullptr; while ((find = strpbrk(last,"<&>\"\'")) != nullptr) { char symb = *find; *find = 0; out->Write(last); *find = symb; last = find+1; if (symb=='<') out->Write("<"); else if (symb=='>') out->Write(">"); else if (symb=='&') out->Write("&"); else if (symb=='\'') out->Write("'"); else out->Write("""); } if (*last) out->Write(last); } //______________________________________________________________________________ void dabc::Xml::SaveNode(XMLNodePointer_t xmlnode, XmlOutputStream* out, int layout, int level) { // stream data of xmlnode to output if (!xmlnode) return; SXmlNode_t* node = (SXmlNode_t*) xmlnode; // this is output for content if (*SXmlNode_t::Name(node) == 0) { out->Write(SXmlNode_t::Name(node)+1); return; } bool issingleline = !node->fChild; if (layout>0) out->Put(' ', level); if (node->fType==kXML_COMMENT) { out->Write(""); if (layout>0) out->Put('\n'); return; } else if (node->fType==kXML_RAWLINE) { out->Write(SXmlNode_t::Name(node)); if (layout>0) out->Put('\n'); return; } out->Put('<'); if (node->fType==kXML_PI_NODE) out->Put('?'); // we suppose that ns is always first attribute if (node->fNs && (node->fNs != node->fAttr)) { out->Write(SXmlAttr_t::Name(node->fNs)+6); out->Put(':'); } out->Write(SXmlNode_t::Name(node)); SXmlAttr_t* attr = node->fAttr; while (attr) { out->Put(' '); char* attrname = SXmlAttr_t::Name(attr); out->Write(attrname); out->Write("=\""); attrname += strlen(attrname) + 1; OutputValue(attrname, out); out->Put('\"'); attr = attr->fNext; } // if single line, close node with "/>" and return if (issingleline) { if (node->fType==kXML_PI_NODE) out->Write("?>"); else out->Write("/>"); if (layout>0) out->Put('\n'); return; } out->Put('>'); // go to next line only if no content inside const char *content = GetNodeContent(xmlnode); if (!content && (layout > 0)) out->Put('\n'); if (content) out->Write(content); SXmlNode_t* child = (SXmlNode_t*) GetChild(xmlnode); while (child) { if (content) { content = nullptr; if (layout > 0) out->Put('\n'); } SaveNode((XMLNodePointer_t) child, out, layout, level+2); child = child->fNext; } // add starting spaces if (!content && (layout > 0)) out->Put(' ',level); out->Write("fNs && (node->fNs != node->fAttr)) { out->Write(SXmlAttr_t::Name(node->fNs)+6); out->Put(':'); } out->Write(SXmlNode_t::Name(node)); out->Put('>'); if (layout > 0) out->Put('\n'); } //______________________________________________________________________________ dabc::XMLNodePointer_t dabc::Xml::ReadNode(XMLNodePointer_t xmlparent, XmlInputStream* inp, int& resvalue) { // Tries to construct xml node from input stream. Node should be // child of xmlparent node or it can be closing tag of xmlparent. // resvalue <= 0 if error // resvalue == 1 if this is endnode of parent // resvalue == 2 if this is child resvalue = 0; if (!inp) return nullptr; if (!inp->SkipSpaces()) { resvalue = -1; return nullptr; } SXmlNode_t* parent = (SXmlNode_t*) xmlparent; SXmlNode_t* node = nullptr; // process comments before we start to analyze any node symbols while (inp->CheckFor(""); if (commentlen < 0) { resvalue = -10; return nullptr; } if (!inp->SkipComments()) { node = (SXmlNode_t*) AllocateNode(commentlen, xmlparent); char* nameptr = SXmlNode_t::Name(node); node->fType = kXML_COMMENT; strncpy(nameptr, inp->fCurrent, commentlen); // here copy only content, there is no padding 0 at the end nameptr+=commentlen; *nameptr = 0; // here we add padding 0 to get normal string } if (!inp->ShiftCurrent(commentlen+3)) { resvalue = -1; return node; } if (!inp->SkipSpaces() && !inp->EndOfStream()) { resvalue = -1; return node; } resvalue = 2; return node; } if (*inp->fCurrent!='<') { // here should be reading of element content // only one entry for content is supported, only before any other childs if (!parent || parent->fChild) { resvalue = -2; return nullptr; } int contlen = inp->LocateContent(); if (contlen < 0) return nullptr; SXmlNode_t* contnode = (SXmlNode_t*) AllocateNode(contlen+1, xmlparent); char* contptr = SXmlNode_t::Name(contnode); *contptr = 0; contptr++; UnpackSpecialCharacters(contptr, inp->fCurrent, contlen); if (!inp->ShiftCurrent(contlen)) return nullptr; resvalue = 2; return contnode; } else // skip "<" symbol if (!inp->ShiftCurrent()) return nullptr; if (*inp->fCurrent=='/') { // this is a starting of closing node if (!inp->ShiftCurrent()) return nullptr; if (!inp->SkipSpaces()) return nullptr; int len = inp->LocateIdentifier(); if (len <= 0) { resvalue = -3; return nullptr; } if (!parent) { resvalue = -4; return nullptr; } if (strncmp(SXmlNode_t::Name(parent), inp->fCurrent, len) != 0) { resvalue = -5; return nullptr; } if (!inp->ShiftCurrent(len)) return nullptr; if (!inp->SkipSpaces()) return nullptr; if (*inp->fCurrent!='>') return nullptr; if (!inp->ShiftCurrent()) return nullptr; if (parent->fNs) TruncateNsExtension((XMLNodePointer_t)parent); inp->SkipSpaces(true); // locate start of next string resvalue = 1; return nullptr; } EXmlNodeType nodetype = kXML_NODE; bool canhaschilds = true; char endsymbol = '/'; // this is case of processing instructions node if (*inp->fCurrent=='?') { if (!inp->ShiftCurrent()) return nullptr; nodetype = kXML_PI_NODE; canhaschilds = false; endsymbol = '?'; } if (!inp->SkipSpaces()) return nullptr; int len = inp->LocateIdentifier(); if (len <= 0) return nullptr; node = (SXmlNode_t*) AllocateNode(len, xmlparent); char* nameptr = SXmlNode_t::Name(node); node->fType = nodetype; strncpy(nameptr, inp->fCurrent, len); // here copied content without padding 0 nameptr+=len; *nameptr = 0; // add 0 to the end char* colon = strchr(SXmlNode_t::Name(node),':'); if (colon && parent) { *colon = 0; node->fNs = (SXmlAttr_t*) FindNs(xmlparent, SXmlNode_t::Name(node)); *colon =':'; } if (!inp->ShiftCurrent(len)) return nullptr; do { if (!inp->SkipSpaces()) return nullptr; char nextsymb = *inp->fCurrent; if (nextsymb==endsymbol) { // this is end of short node like if (!inp->ShiftCurrent()) return nullptr; if (*inp->fCurrent=='>') { if (!inp->ShiftCurrent()) return nullptr; if (node->fNs) TruncateNsExtension((XMLNodePointer_t) node); inp->SkipSpaces(true); // locate start of next string resvalue = 2; return node; } else { return nullptr; } } else if (nextsymb=='>') { // this is end of parent node, lets find all childs if (!canhaschilds) { resvalue = -11; return nullptr; } if (!inp->ShiftCurrent()) return nullptr; do { ReadNode(node, inp, resvalue); } while (resvalue == 2); if (resvalue == 1) { resvalue = 2; return node; } else { return nullptr; } } else { int attrlen = inp->LocateIdentifier(); if (attrlen <= 0) { resvalue = -6; return nullptr; } char* valuestart = inp->fCurrent+attrlen; int valuelen = inp->LocateAttributeValue(valuestart); if (valuelen < 3) { resvalue = -7; return nullptr; } SXmlAttr_t* attr = (SXmlAttr_t*) AllocateAttr(attrlen, valuelen-3, (XMLNodePointer_t) node); char* attrname = SXmlAttr_t::Name(attr); strncpy(attrname, inp->fCurrent, attrlen); attrname+=attrlen; *attrname = 0; attrname++; UnpackSpecialCharacters(attrname, valuestart+2, valuelen-3); if (!inp->ShiftCurrent(attrlen+valuelen)) return nullptr; attrname = SXmlAttr_t::Name(attr); if ((strlen(attrname)>6) && (strstr(attrname,"xmlns:")==attrname)) { if (strcmp(SXmlNode_t::Name(node), attrname + 6) != 0) { resvalue = -8; //return nullptr; } if (node->fNs) { resvalue = -9; //return nullptr; } node->fNs = attr; } } } while (true); return nullptr; } //______________________________________________________________________________ void dabc::Xml::DisplayError(int error, int linenumber) { // Displays xml parsing error switch(error) { case -11: EOUT("Node cannot be closed with > symbol at line %d, for instance node", linenumber); break; case -10: EOUT("Error in xml comments definition at line %d, must be ", linenumber); break; case -9: EOUT("Multiple name space definitions not allowed, line %d", linenumber); break; case -8: EOUT("Invalid namespace specification, line %d", linenumber); break; case -7: EOUT("Invalid attribute value, line %d", linenumber); break; case -6: EOUT("Invalid identifier for node attribute, line %d", linenumber); break; case -5: EOUT("Mismatch between open and close nodes, line %d", linenumber); break; case -4: EOUT("Unexpected close node, line %d", linenumber); break; case -3: EOUT("Valid identifier for close node is missing, line %d", linenumber); break; case -2: EOUT("No multiple content entries allowed, line %d", linenumber); break; case -1: EOUT("Unexpected end of xml file"); break; default: EOUT("XML syntax error at line %d", linenumber); break; } }