X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fxml%2B%2B.cc;h=80fc88242dc9884dc79b5351a10bcd37830c981c;hb=9fb78d091de6abcf155e73728b15ea3c311e124e;hp=a42923132bc3042eef8d12fef9a5f32e04dc56a1;hpb=6b0d22268b85f7033eb7ddcfe9e50e21b86eec34;p=ardour.git diff --git a/libs/pbd/xml++.cc b/libs/pbd/xml++.cc index a42923132b..80fc88242d 100644 --- a/libs/pbd/xml++.cc +++ b/libs/pbd/xml++.cc @@ -2,49 +2,62 @@ * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and * are covered by the GNU Lesser General Public License, which should be * included with libxml++ as the file COPYING. + * Modified for Ardour and released under the same terms. */ -#include +#include + +#include "pbd/stacktrace.h" +#include "pbd/xml++.h" + #include #include #include -#define XML_VERSION "1.0" +xmlChar* xml_version = xmlCharStrdup("1.0"); + +using namespace std; -static XMLNode *readnode(xmlNodePtr); -static void writenode(xmlDocPtr, XMLNode *, xmlNodePtr, int); -static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string xpath); +static XMLNode* readnode(xmlNodePtr); +static void writenode(xmlDocPtr, XMLNode*, xmlNodePtr, int); +static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath); -XMLTree::XMLTree() - : _filename(), - _root(0), - _compression(0) -{ +XMLTree::XMLTree() + : _filename() + , _root(0) + , _doc (0) + , _compression(0) +{ } -XMLTree::XMLTree(const string &fn, bool validate) - : _filename(fn), - _root(0), - _compression(0) -{ - read_internal(validate); +XMLTree::XMLTree(const string& fn, bool validate) + : _filename(fn) + , _root(0) + , _doc (0) + , _compression(0) +{ + read_internal(validate); } -XMLTree::XMLTree(const XMLTree * from) +XMLTree::XMLTree(const XMLTree* from) + : _filename(from->filename()) + , _root(new XMLNode(*from->root())) + , _doc (xmlCopyDoc (from->_doc, 1)) + , _compression(from->compression()) { - _filename = from->filename(); - _root = new XMLNode(*from->root()); - _compression = from->compression(); + } XMLTree::~XMLTree() { - if (_root) { - delete _root; + delete _root; + + if (_doc) { + xmlFreeDoc (_doc); } } -int +int XMLTree::set_compression(int c) { if (c > 9) { @@ -52,286 +65,332 @@ XMLTree::set_compression(int c) } else if (c < 0) { c = 0; } - + _compression = c; - + return _compression; } -bool +bool XMLTree::read_internal(bool validate) { //shouldnt be used anywhere ATM, remove if so! assert(!validate); - if (_root) { - delete _root; - _root = 0; - } - - xmlParserCtxtPtr ctxt; /* the parser context */ - xmlDocPtr doc; /* the resulting document tree */ - + + delete _root; + _root = 0; + + if (_doc) { + xmlFreeDoc (_doc); + _doc = 0; + } + + /* create a parser context */ + xmlParserCtxtPtr ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) { + return false; + } + xmlKeepBlanksDefault(0); /* parse the file, activating the DTD validation option */ - if(validate) { - /* create a parser context */ - ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) { - return false; - } - doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_DTDVALID); + if (validate) { + _doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_DTDVALID); } else { - doc = xmlParseFile(_filename.c_str()); + _doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_HUGE); } - + /* check if parsing suceeded */ - if (doc == NULL) { - if(validate) { - xmlFreeParserCtxt(ctxt); - } + if (_doc == NULL) { + xmlFreeParserCtxt(ctxt); return false; } else { - /* check if validation suceeded */ + /* check if validation suceeded */ if (validate && ctxt->valid == 0) { xmlFreeParserCtxt(ctxt); - xmlFreeDoc(doc); - xmlCleanupParser(); throw XMLException("Failed to validate document " + _filename); } } - - _root = readnode(xmlDocGetRootElement(doc)); - + + _root = readnode(xmlDocGetRootElement(_doc)); + /* free up the parser context */ - if(validate) { - xmlFreeParserCtxt(ctxt); - } - xmlFreeDoc(doc); - xmlCleanupParser(); - + xmlFreeParserCtxt(ctxt); + return true; } -bool -XMLTree::read_buffer(const string & buffer) +bool +XMLTree::read_buffer(const string& buffer) { xmlDocPtr doc; - + _filename = ""; - - if (_root) { - delete _root; - _root = 0; - } - - doc = xmlParseMemory((char *) buffer.c_str(), buffer.length()); + + delete _root; + _root = 0; + + doc = xmlParseMemory(const_cast(buffer.c_str()), buffer.length()); if (!doc) { return false; } - + _root = readnode(xmlDocGetRootElement(doc)); xmlFreeDoc(doc); - + return true; } -bool -XMLTree::write(void) const +bool +XMLTree::write() const { xmlDocPtr doc; XMLNodeList children; int result; - + xmlKeepBlanksDefault(0); - doc = xmlNewDoc((xmlChar *) XML_VERSION); + doc = xmlNewDoc(xml_version); xmlSetDocCompressMode(doc, _compression); writenode(doc, _root, doc->children, 1); result = xmlSaveFormatFileEnc(_filename.c_str(), doc, "UTF-8", 1); +#ifndef NDEBUG + if (result == -1) { + xmlErrorPtr xerr = xmlGetLastError (); + if (!xerr) { + std::cerr << "unknown XML error during xmlSaveFormatFileEnc()." << std::endl; + } else { + std::cerr << "xmlSaveFormatFileEnc: error" + << " domain: " << xerr->domain + << " code: " << xerr->code + << " msg: " << xerr->message + << std::endl; + } + } +#endif xmlFreeDoc(doc); - + if (result == -1) { return false; } - + return true; } void XMLTree::debug(FILE* out) const { +#ifdef LIBXML_DEBUG_ENABLED xmlDocPtr doc; XMLNodeList children; xmlKeepBlanksDefault(0); - doc = xmlNewDoc((xmlChar *) XML_VERSION); + doc = xmlNewDoc(xml_version); xmlSetDocCompressMode(doc, _compression); writenode(doc, _root, doc->children, 1); xmlDebugDumpDocument (out, doc); xmlFreeDoc(doc); +#endif } -const string & -XMLTree::write_buffer(void) const +const string& +XMLTree::write_buffer() const { static string retval; - char *ptr; + char* ptr; int len; xmlDocPtr doc; XMLNodeList children; - + xmlKeepBlanksDefault(0); - doc = xmlNewDoc((xmlChar *) XML_VERSION); + doc = xmlNewDoc(xml_version); xmlSetDocCompressMode(doc, _compression); writenode(doc, _root, doc->children, 1); xmlDocDumpMemory(doc, (xmlChar **) & ptr, &len); xmlFreeDoc(doc); - + retval = ptr; - + free(ptr); - + return retval; } -XMLNode::XMLNode(const string & n) - : _name(n), _is_content(false), _content(string()) +XMLNode::XMLNode(const string& n) + : _name(n) + , _is_content(false) { } -XMLNode::XMLNode(const string & n, const string & c) - :_name(n), _is_content(true), _content(c) +XMLNode::XMLNode(const string& n, const string& c) + : _name(n) + , _is_content(true) + , _content(c) { } XMLNode::XMLNode(const XMLNode& from) { - XMLPropertyList props; - XMLPropertyIterator curprop; - XMLNodeList nodes; - XMLNodeIterator curnode; - - _name = from.name(); - set_content(from.content()); - - props = from.properties(); - for (curprop = props.begin(); curprop != props.end(); ++curprop) { - add_property((*curprop)->name().c_str(), (*curprop)->value()); - } - - nodes = from.children(); - for (curnode = nodes.begin(); curnode != nodes.end(); ++curnode) { - add_child_copy(**curnode); - } + *this = from; } XMLNode::~XMLNode() +{ + clear_lists (); +} + +void +XMLNode::clear_lists () { XMLNodeIterator curchild; XMLPropertyIterator curprop; - + + _selected_children.clear (); + _propmap.clear (); + for (curchild = _children.begin(); curchild != _children.end(); ++curchild) { delete *curchild; } - + + _children.clear (); + for (curprop = _proplist.begin(); curprop != _proplist.end(); ++curprop) { delete *curprop; } + + _proplist.clear (); } -const string & -XMLNode::set_content(const string & c) +XMLNode& +XMLNode::operator= (const XMLNode& from) +{ + if (&from != this) { + + XMLPropertyList props; + XMLPropertyIterator curprop; + XMLNodeList nodes; + XMLNodeIterator curnode; + + clear_lists (); + + _name = from.name(); + set_content(from.content()); + + props = from.properties(); + for (curprop = props.begin(); curprop != props.end(); ++curprop) { + add_property((*curprop)->name().c_str(), (*curprop)->value()); + } + + nodes = from.children(); + for (curnode = nodes.begin(); curnode != nodes.end(); ++curnode) { + add_child_copy(**curnode); + } + } + + return *this; +} + +const string& +XMLNode::set_content(const string& c) { if (c.empty()) { _is_content = false; } else { _is_content = true; } - + _content = c; - + return _content; } XMLNode* -XMLNode::child (const char *name) const +XMLNode::child (const char* name) const { /* returns first child matching name */ XMLNodeConstIterator cur; - + if (name == 0) { return 0; } - + for (cur = _children.begin(); cur != _children.end(); ++cur) { if ((*cur)->name() == name) { return *cur; } } - + return 0; } -const XMLNodeList & +const XMLNodeList& XMLNode::children(const string& n) const { /* returns all children matching name */ XMLNodeConstIterator cur; - + if (n.empty()) { return _children; } _selected_children.clear(); - + for (cur = _children.begin(); cur != _children.end(); ++cur) { if ((*cur)->name() == n) { _selected_children.insert(_selected_children.end(), *cur); } } - + return _selected_children; } -XMLNode * -XMLNode::add_child(const char * n) +XMLNode* +XMLNode::add_child(const char* n) { return add_child_copy(XMLNode (n)); } void -XMLNode::add_child_nocopy (XMLNode& n) +XMLNode::add_child_nocopy(XMLNode& n) { _children.insert(_children.end(), &n); } -XMLNode * +XMLNode* XMLNode::add_child_copy(const XMLNode& n) { - XMLNode *copy = new XMLNode (n); + XMLNode *copy = new XMLNode(n); _children.insert(_children.end(), copy); return copy; } -boost::shared_ptr -XMLNode::find(const string xpath) const +boost::shared_ptr +XMLTree::find(const string xpath, XMLNode* node) const { - xmlDocPtr doc = xmlNewDoc((xmlChar *) XML_VERSION); - writenode(doc, (XMLNode *) this, doc->children, 1); - xmlXPathContext* ctxt = xmlXPathNewContext(doc); - - boost::shared_ptr result = + xmlXPathContext* ctxt; + xmlDocPtr doc = 0; + + if (node) { + doc = xmlNewDoc(xml_version); + writenode(doc, node, doc->children, 1); + ctxt = xmlXPathNewContext(doc); + } else { + ctxt = xmlXPathNewContext(_doc); + } + + boost::shared_ptr result = boost::shared_ptr(find_impl(ctxt, xpath)); - + xmlXPathFreeContext(ctxt); - xmlFreeDoc(doc); - + if (doc) { + xmlFreeDoc (doc); + } + return result; } -std::string +std::string XMLNode::attribute_value() { XMLNodeList children = this->children(); @@ -342,14 +401,39 @@ XMLNode::attribute_value() return child->content(); } -XMLNode * -XMLNode::add_content(const string & c) +XMLNode* +XMLNode::add_content(const string& c) { return add_child_copy(XMLNode (string(), c)); } +XMLProperty const * +XMLNode::property(const char* n) const +{ + string ns(n); + map::const_iterator iter; + + if ((iter = _propmap.find(ns)) != _propmap.end()) { + return iter->second; + } + + return 0; +} + +XMLProperty const * +XMLNode::property(const string& ns) const +{ + map::const_iterator iter; + + if ((iter = _propmap.find(ns)) != _propmap.end()) { + return iter->second; + } + + return 0; +} + XMLProperty * -XMLNode::property(const char * n) +XMLNode::property(const char* n) { string ns(n); map::iterator iter; @@ -362,26 +446,40 @@ XMLNode::property(const char * n) } XMLProperty * -XMLNode::property(const string & ns) +XMLNode::property(const string& ns) { map::iterator iter; if ((iter = _propmap.find(ns)) != _propmap.end()) { return iter->second; } - + return 0; } -XMLProperty * -XMLNode::add_property(const char * n, const string & v) +bool +XMLNode::has_property_with_value (const string& key, const string& value) const +{ + map::const_iterator iter = _propmap.find(key); + if (iter != _propmap.end()) { + const XMLProperty* p = (iter->second); + return (p && p->value() == value); + } + return false; +} + +XMLProperty* +XMLNode::add_property(const char* n, const string& v) { string ns(n); - if(_propmap.find(ns) != _propmap.end()){ - remove_property(ns); + map::iterator iter; + + if ((iter = _propmap.find(ns)) != _propmap.end()) { + iter->second->set_value (v); + return iter->second; } - XMLProperty *tmp = new XMLProperty(ns, v); + XMLProperty* tmp = new XMLProperty(ns, v); if (!tmp) { return 0; @@ -393,88 +491,94 @@ XMLNode::add_property(const char * n, const string & v) return tmp; } -XMLProperty * -XMLNode::add_property(const char * n, const char * v) +XMLProperty* +XMLNode::add_property(const char* n, const char* v) { string vs(v); return add_property(n, vs); } -XMLProperty * -XMLNode::add_property(const char *name, const long value) +XMLProperty* +XMLNode::add_property(const char* name, const long value) { - static char str[1024]; - snprintf(str, 1024, "%ld", value); + char str[64]; + snprintf(str, sizeof(str), "%ld", value); return add_property(name, str); } -void -XMLNode::remove_property(const string & n) +void +XMLNode::remove_property(const string& n) { if (_propmap.find(n) != _propmap.end()) { - _proplist.remove(_propmap[n]); + XMLProperty* p = _propmap[n]; + XMLPropertyIterator i = std::find(_proplist.begin(), _proplist.end(), p); + if (i != _proplist.end ()) { + _proplist.erase (i); + } + delete p; _propmap.erase(n); } } -void -XMLNode::remove_nodes(const string & n) +/** Remove any property with the given name from this node and its children */ +void +XMLNode::remove_property_recursively(const string& n) +{ + remove_property (n); + for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) { + (*i)->remove_property_recursively (n); + } +} + +void +XMLNode::remove_nodes(const string& n) { XMLNodeIterator i = _children.begin(); - XMLNodeIterator tmp; - while (i != _children.end()) { - tmp = i; - ++tmp; if ((*i)->name() == n) { - _children.erase (i); + i = _children.erase (i); + } else { + ++i; } - i = tmp; } } -void -XMLNode::remove_nodes_and_delete(const string & n) +void +XMLNode::remove_nodes_and_delete(const string& n) { XMLNodeIterator i = _children.begin(); - XMLNodeIterator tmp; - + while (i != _children.end()) { - tmp = i; - ++tmp; if ((*i)->name() == n) { delete *i; - _children.erase (i); + i = _children.erase (i); + } else { + ++i; } - i = tmp; } } void -XMLNode::remove_nodes_and_delete(const string& propname, const string& val) +XMLNode::remove_nodes_and_delete(const string& propname, const string& val) { XMLNodeIterator i = _children.begin(); - XMLNodeIterator tmp; - XMLProperty* prop; + XMLProperty const * prop; while (i != _children.end()) { - tmp = i; - ++tmp; - prop = (*i)->property(propname); - if(prop && prop->value() == val) { + if (prop && prop->value() == val) { delete *i; - _children.erase(i); + i = _children.erase(i); + } else { + ++i; } - - i = tmp; } } -XMLProperty::XMLProperty(const string &n, const string &v) - : _name(n), - _value(v) -{ +XMLProperty::XMLProperty(const string& n, const string& v) + : _name(n) + , _value(v) +{ // Normalize property name (replace '_' with '-' as old session are inconsistent) for (size_t i = 0; i < _name.length(); ++i) { if (_name[i] == '_') { @@ -487,86 +591,84 @@ XMLProperty::~XMLProperty() { } -static XMLNode * +static XMLNode* readnode(xmlNodePtr node) { string name, content; xmlNodePtr child; - XMLNode *tmp; + XMLNode* tmp; xmlAttrPtr attr; - + if (node->name) { - name = (char *) node->name; + name = (const char*)node->name; } - + tmp = new XMLNode(name); - + for (attr = node->properties; attr; attr = attr->next) { content = ""; if (attr->children) { - content = (char *) attr->children->content; + content = (char*)attr->children->content; } - tmp->add_property((char *) attr->name, content); + tmp->add_property((const char*)attr->name, content); } - + if (node->content) { - tmp->set_content((char *) node->content); + tmp->set_content((char*)node->content); } else { tmp->set_content(string()); } - + for (child = node->children; child; child = child->next) { tmp->add_child_nocopy (*readnode(child)); } - + return tmp; } -static void -writenode(xmlDocPtr doc, XMLNode * n, xmlNodePtr p, int root = 0) +static void +writenode(xmlDocPtr doc, XMLNode* n, xmlNodePtr p, int root = 0) { XMLPropertyList props; XMLPropertyIterator curprop; XMLNodeList children; XMLNodeIterator curchild; xmlNodePtr node; - + if (root) { - node = doc->children = xmlNewDocNode(doc, 0, (xmlChar *) n->name().c_str(), 0); + node = doc->children = xmlNewDocNode(doc, 0, (const xmlChar*) n->name().c_str(), 0); } else { - node = xmlNewChild(p, 0, (xmlChar *) n->name().c_str(), 0); + node = xmlNewChild(p, 0, (const xmlChar*) n->name().c_str(), 0); } - + if (n->is_content()) { node->type = XML_TEXT_NODE; - xmlNodeSetContentLen(node, (const xmlChar *) n->content().c_str(), n->content().length()); + xmlNodeSetContentLen(node, (const xmlChar*)n->content().c_str(), n->content().length()); } - + props = n->properties(); for (curprop = props.begin(); curprop != props.end(); ++curprop) { - xmlSetProp(node, (xmlChar *) (*curprop)->name().c_str(), (xmlChar *) (*curprop)->value().c_str()); + xmlSetProp(node, (const xmlChar*) (*curprop)->name().c_str(), (const xmlChar*) (*curprop)->value().c_str()); } - + children = n->children(); for (curchild = children.begin(); curchild != children.end(); ++curchild) { writenode(doc, *curchild, node); } } -static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string xpath) +static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath) { xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt); - if(!result) - { + if (!result) { xmlXPathFreeContext(ctxt); xmlFreeDoc(ctxt->doc); throw XMLException("Invalid XPath: " + xpath); } - if(result->type != XPATH_NODESET) - { + if (result->type != XPATH_NODESET) { xmlXPathFreeObject(result); xmlXPathFreeContext(ctxt); xmlFreeDoc(ctxt->doc); @@ -576,15 +678,12 @@ static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string xpath) xmlNodeSet* nodeset = result->nodesetval; XMLSharedNodeList* nodes = new XMLSharedNodeList(); - if( nodeset ) - { + if (nodeset) { for (int i = 0; i < nodeset->nodeNr; ++i) { XMLNode* node = readnode(nodeset->nodeTab[i]); nodes->push_back(boost::shared_ptr(node)); } - } - else - { + } else { // return empty set } @@ -593,3 +692,23 @@ static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string xpath) return nodes; } +/** Dump a node, its properties and children to a stream */ +void +XMLNode::dump (ostream& s, string p) const +{ + if (_is_content) { + s << p << " " << content() << "\n"; + } else { + s << p << "<" << _name; + for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) { + s << " " << (*i)->name() << "=\"" << (*i)->value() << "\""; + } + s << ">\n"; + + for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) { + (*i)->dump (s, p + " "); + } + + s << p << "\n"; + } +}