xml_libxml2.cpp (3747B)
1 /* ReaPack: Package manager for REAPER 2 * Copyright (C) 2015-2025 Christian Fillion 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "xml.hpp" 19 20 #include <cstdio> 21 #include <cstring> 22 23 #include <libxml/parser.h> 24 25 constexpr int LIBXML2_OPTIONS = 26 XML_PARSE_NOBLANKS | XML_PARSE_NOERROR | XML_PARSE_NOWARNING; 27 28 struct XmlDocument::Impl { xmlDoc *doc; }; 29 struct XmlNode::Impl { xmlNode *node; }; 30 31 static int readCallback(void *context, char *buffer, const int size) 32 { 33 auto stream = static_cast<std::istream *>(context); 34 const std::streamsize bytes = stream->read(buffer, size).gcount(); 35 return static_cast<int>(bytes); 36 } 37 38 XmlDocument::XmlDocument(std::istream &stream) : m_impl{new Impl} 39 { 40 xmlResetLastError(); 41 42 m_impl->doc = 43 xmlReadIO(&readCallback, nullptr, static_cast<void *>(&stream), 44 nullptr, nullptr, LIBXML2_OPTIONS); 45 } 46 47 XmlDocument::XmlDocument(XmlDocument &&) = default; 48 49 XmlDocument::~XmlDocument() 50 { 51 if(m_impl->doc) 52 xmlFreeDoc(m_impl->doc); 53 } 54 55 XmlDocument::operator bool() const 56 { 57 return m_impl->doc && xmlGetLastError() == nullptr; 58 } 59 60 const char *XmlDocument::error() const 61 { 62 if(const xmlError *error = xmlGetLastError()) { 63 const size_t length = strlen(error->message); 64 if(length > 1) { 65 // remove trailing newline 66 char &tail = error->message[length - 1]; 67 if(tail == '\n') 68 tail = 0; 69 } 70 71 return error->message; 72 } 73 74 return nullptr; 75 } 76 77 XmlNode XmlDocument::root() const 78 { 79 return xmlDocGetRootElement(m_impl->doc); 80 } 81 82 XmlNode::XmlNode(void *node) : m_impl(new Impl{static_cast<xmlNode *>(node)}) {} 83 XmlNode::XmlNode(const XmlNode ©) { *this = copy; } 84 XmlNode::~XmlNode() = default; 85 86 XmlNode &XmlNode::operator=(const XmlNode &other) 87 { 88 m_impl.reset(new Impl(*other.m_impl)); 89 return *this; 90 } 91 92 XmlNode::operator bool() const 93 { 94 return m_impl->node != nullptr; 95 } 96 97 const char *XmlNode::name() const 98 { 99 return reinterpret_cast<const char *>(m_impl->node->name); 100 } 101 102 XmlString XmlNode::attribute(const char *name) const 103 { 104 return xmlGetProp(m_impl->node, reinterpret_cast<const xmlChar *>(name)); 105 } 106 107 bool XmlNode::attribute(const char *name, int *output) const 108 { 109 if(const XmlString &value = attribute(name)) { 110 return sscanf(*value, "%d", output) == 1; 111 } 112 113 return false; 114 } 115 116 XmlString XmlNode::text() const 117 { 118 return m_impl->node->children ? xmlNodeGetContent(m_impl->node) : nullptr; 119 } 120 121 XmlNode XmlNode::firstChild(const char *element) const 122 { 123 for(xmlNode *node = m_impl->node->children; node; node = node->next) { 124 if(node->type == XML_ELEMENT_NODE && (!element || 125 !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>(element)))) 126 return node; 127 } 128 129 return nullptr; 130 } 131 132 XmlNode XmlNode::nextSibling(const char *element) const 133 { 134 for(xmlNode *node = m_impl->node->next; node; node = node->next) { 135 if(node->type == XML_ELEMENT_NODE && (!element || 136 !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>(element)))) 137 return node; 138 } 139 140 return nullptr; 141 } 142 143 XmlString::XmlString(const void *str) : m_str(str) {} 144 XmlString::~XmlString() { xmlFree(const_cast<void *>(m_str)); }