reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

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 &copy) { *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)); }