Path: blob/main/contrib/llvm-project/lldb/source/Host/common/XML.cpp
39606 views
//===-- XML.cpp -----------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/Config.h"9#include "lldb/Host/XML.h"1011#include "llvm/ADT/StringExtras.h"1213using namespace lldb;14using namespace lldb_private;1516#pragma mark-- XMLDocument1718XMLDocument::XMLDocument() = default;1920XMLDocument::~XMLDocument() { Clear(); }2122void XMLDocument::Clear() {23#if LLDB_ENABLE_LIBXML224if (m_document) {25xmlDocPtr doc = m_document;26m_document = nullptr;27xmlFreeDoc(doc);28}29#endif30}3132bool XMLDocument::IsValid() const { return m_document != nullptr; }3334void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {35XMLDocument *document = (XMLDocument *)ctx;36va_list args;37va_start(args, format);38document->m_errors.PrintfVarArg(format, args);39document->m_errors.EOL();40va_end(args);41}4243bool XMLDocument::ParseFile(const char *path) {44#if LLDB_ENABLE_LIBXML245Clear();46xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);47m_document = xmlParseFile(path);48xmlSetGenericErrorFunc(nullptr, nullptr);49#endif50return IsValid();51}5253bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,54const char *url) {55#if LLDB_ENABLE_LIBXML256Clear();57xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);58m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);59xmlSetGenericErrorFunc(nullptr, nullptr);60#endif61return IsValid();62}6364XMLNode XMLDocument::GetRootElement(const char *required_name) {65#if LLDB_ENABLE_LIBXML266if (IsValid()) {67XMLNode root_node(xmlDocGetRootElement(m_document));68if (required_name) {69llvm::StringRef actual_name = root_node.GetName();70if (actual_name == required_name)71return root_node;72} else {73return root_node;74}75}76#endif77return XMLNode();78}7980llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }8182bool XMLDocument::XMLEnabled() {83#if LLDB_ENABLE_LIBXML284return true;85#else86return false;87#endif88}8990#pragma mark-- XMLNode9192XMLNode::XMLNode() = default;9394XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}9596XMLNode::~XMLNode() = default;9798void XMLNode::Clear() { m_node = nullptr; }99100XMLNode XMLNode::GetParent() const {101#if LLDB_ENABLE_LIBXML2102if (IsValid())103return XMLNode(m_node->parent);104else105return XMLNode();106#else107return XMLNode();108#endif109}110111XMLNode XMLNode::GetSibling() const {112#if LLDB_ENABLE_LIBXML2113if (IsValid())114return XMLNode(m_node->next);115else116return XMLNode();117#else118return XMLNode();119#endif120}121122XMLNode XMLNode::GetChild() const {123#if LLDB_ENABLE_LIBXML2124125if (IsValid())126return XMLNode(m_node->children);127else128return XMLNode();129#else130return XMLNode();131#endif132}133134std::string XMLNode::GetAttributeValue(const char *name,135const char *fail_value) const {136std::string attr_value;137#if LLDB_ENABLE_LIBXML2138if (IsValid()) {139xmlChar *value = xmlGetProp(m_node, (const xmlChar *)name);140if (value) {141attr_value = (const char *)value;142xmlFree(value);143}144} else {145if (fail_value)146attr_value = fail_value;147}148#else149if (fail_value)150attr_value = fail_value;151#endif152return attr_value;153}154155bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,156uint64_t fail_value, int base) const {157value = fail_value;158return llvm::to_integer(GetAttributeValue(name, ""), value, base);159}160161void XMLNode::ForEachChildNode(NodeCallback const &callback) const {162#if LLDB_ENABLE_LIBXML2163if (IsValid())164GetChild().ForEachSiblingNode(callback);165#endif166}167168void XMLNode::ForEachChildElement(NodeCallback const &callback) const {169#if LLDB_ENABLE_LIBXML2170XMLNode child = GetChild();171if (child)172child.ForEachSiblingElement(callback);173#endif174}175176void XMLNode::ForEachChildElementWithName(const char *name,177NodeCallback const &callback) const {178#if LLDB_ENABLE_LIBXML2179XMLNode child = GetChild();180if (child)181child.ForEachSiblingElementWithName(name, callback);182#endif183}184185void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {186#if LLDB_ENABLE_LIBXML2187188if (IsValid()) {189for (xmlAttrPtr attr = m_node->properties; attr != nullptr;190attr = attr->next) {191// check if name matches192if (attr->name) {193// check child is a text node194xmlNodePtr child = attr->children;195if (child->type == XML_TEXT_NODE) {196llvm::StringRef attr_value;197if (child->content)198attr_value = llvm::StringRef((const char *)child->content);199if (!callback(llvm::StringRef((const char *)attr->name), attr_value))200return;201}202}203}204}205#endif206}207208void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {209#if LLDB_ENABLE_LIBXML2210211if (IsValid()) {212// iterate through all siblings213for (xmlNodePtr node = m_node; node; node = node->next) {214if (!callback(XMLNode(node)))215return;216}217}218#endif219}220221void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {222#if LLDB_ENABLE_LIBXML2223224if (IsValid()) {225// iterate through all siblings226for (xmlNodePtr node = m_node; node; node = node->next) {227// we are looking for element nodes only228if (node->type != XML_ELEMENT_NODE)229continue;230231if (!callback(XMLNode(node)))232return;233}234}235#endif236}237238void XMLNode::ForEachSiblingElementWithName(239const char *name, NodeCallback const &callback) const {240#if LLDB_ENABLE_LIBXML2241242if (IsValid()) {243// iterate through all siblings244for (xmlNodePtr node = m_node; node; node = node->next) {245// we are looking for element nodes only246if (node->type != XML_ELEMENT_NODE)247continue;248249// If name is nullptr, we take all nodes of type "t", else just the ones250// whose name matches251if (name) {252if (strcmp((const char *)node->name, name) != 0)253continue; // Name mismatch, ignore this one254} else {255if (node->name)256continue; // nullptr name specified and this element has a name,257// ignore this one258}259260if (!callback(XMLNode(node)))261return;262}263}264#endif265}266267llvm::StringRef XMLNode::GetName() const {268#if LLDB_ENABLE_LIBXML2269if (IsValid()) {270if (m_node->name)271return llvm::StringRef((const char *)m_node->name);272}273#endif274return llvm::StringRef();275}276277bool XMLNode::GetElementText(std::string &text) const {278text.clear();279#if LLDB_ENABLE_LIBXML2280if (IsValid()) {281bool success = false;282if (m_node->type == XML_ELEMENT_NODE) {283// check child is a text node284for (xmlNodePtr node = m_node->children; node != nullptr;285node = node->next) {286if (node->type == XML_TEXT_NODE) {287text.append((const char *)node->content);288success = true;289}290}291}292return success;293}294#endif295return false;296}297298bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,299int base) const {300std::string text;301302value = fail_value;303return GetElementText(text) && llvm::to_integer(text, value, base);304}305306bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {307std::string text;308309value = fail_value;310return GetElementText(text) && llvm::to_float(text, value);311}312313bool XMLNode::NameIs(const char *name) const {314#if LLDB_ENABLE_LIBXML2315316if (IsValid()) {317// In case we are looking for a nullptr name or an exact pointer match318if (m_node->name == (const xmlChar *)name)319return true;320if (m_node->name)321return strcmp((const char *)m_node->name, name) == 0;322}323#endif324return false;325}326327XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {328XMLNode result_node;329330#if LLDB_ENABLE_LIBXML2331ForEachChildElementWithName(332name, [&result_node](const XMLNode &node) -> bool {333result_node = node;334// Stop iterating, we found the node we wanted335return false;336});337#endif338339return result_node;340}341342bool XMLNode::IsValid() const { return m_node != nullptr; }343344bool XMLNode::IsElement() const {345#if LLDB_ENABLE_LIBXML2346if (IsValid())347return m_node->type == XML_ELEMENT_NODE;348#endif349return false;350}351352XMLNode XMLNode::GetElementForPath(const NamePath &path) {353#if LLDB_ENABLE_LIBXML2354355if (IsValid()) {356if (path.empty())357return *this;358else {359XMLNode node = FindFirstChildElementWithName(path[0].c_str());360const size_t n = path.size();361for (size_t i = 1; node && i < n; ++i)362node = node.FindFirstChildElementWithName(path[i].c_str());363return node;364}365}366#endif367368return XMLNode();369}370371#pragma mark-- ApplePropertyList372373ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}374375ApplePropertyList::ApplePropertyList(const char *path)376: m_xml_doc(), m_dict_node() {377ParseFile(path);378}379380ApplePropertyList::~ApplePropertyList() = default;381382llvm::StringRef ApplePropertyList::GetErrors() const {383return m_xml_doc.GetErrors();384}385386bool ApplePropertyList::ParseFile(const char *path) {387if (m_xml_doc.ParseFile(path)) {388XMLNode plist = m_xml_doc.GetRootElement("plist");389if (plist) {390plist.ForEachChildElementWithName("dict",391[this](const XMLNode &dict) -> bool {392this->m_dict_node = dict;393return false; // Stop iterating394});395return (bool)m_dict_node;396}397}398return false;399}400401bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }402403bool ApplePropertyList::GetValueAsString(const char *key,404std::string &value) const {405XMLNode value_node = GetValueNode(key);406if (value_node)407return ApplePropertyList::ExtractStringFromValueNode(value_node, value);408return false;409}410411XMLNode ApplePropertyList::GetValueNode(const char *key) const {412XMLNode value_node;413#if LLDB_ENABLE_LIBXML2414415if (IsValid()) {416m_dict_node.ForEachChildElementWithName(417"key", [key, &value_node](const XMLNode &key_node) -> bool {418std::string key_name;419if (key_node.GetElementText(key_name)) {420if (key_name == key) {421value_node = key_node.GetSibling();422while (value_node && !value_node.IsElement())423value_node = value_node.GetSibling();424return false; // Stop iterating425}426}427return true; // Keep iterating428});429}430#endif431return value_node;432}433434bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,435std::string &value) {436value.clear();437#if LLDB_ENABLE_LIBXML2438if (node.IsValid()) {439llvm::StringRef element_name = node.GetName();440if (element_name == "true" || element_name == "false") {441// The text value _is_ the element name itself...442value = element_name.str();443return true;444} else if (element_name == "dict" || element_name == "array")445return false; // dictionaries and arrays have no text value, so we fail446else447return node.GetElementText(value);448}449#endif450return false;451}452453#if LLDB_ENABLE_LIBXML2454455static StructuredData::ObjectSP CreatePlistValue(XMLNode node) {456llvm::StringRef element_name = node.GetName();457if (element_name == "array") {458std::shared_ptr<StructuredData::Array> array_sp(459new StructuredData::Array());460node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {461array_sp->AddItem(CreatePlistValue(node));462return true; // Keep iterating through all child elements of the array463});464return array_sp;465} else if (element_name == "dict") {466XMLNode key_node;467std::shared_ptr<StructuredData::Dictionary> dict_sp(468new StructuredData::Dictionary());469node.ForEachChildElement(470[&key_node, &dict_sp](const XMLNode &node) -> bool {471if (node.NameIs("key")) {472// This is a "key" element node473key_node = node;474} else {475// This is a value node476if (key_node) {477std::string key_name;478key_node.GetElementText(key_name);479dict_sp->AddItem(key_name, CreatePlistValue(node));480key_node.Clear();481}482}483return true; // Keep iterating through all child elements of the484// dictionary485});486return dict_sp;487} else if (element_name == "real") {488double value = 0.0;489node.GetElementTextAsFloat(value);490return StructuredData::ObjectSP(new StructuredData::Float(value));491} else if (element_name == "integer") {492uint64_t value = 0;493node.GetElementTextAsUnsigned(value, 0, 0);494return StructuredData::ObjectSP(new StructuredData::UnsignedInteger(value));495} else if ((element_name == "string") || (element_name == "data") ||496(element_name == "date")) {497std::string text;498node.GetElementText(text);499return StructuredData::ObjectSP(500new StructuredData::String(std::move(text)));501} else if (element_name == "true") {502return StructuredData::ObjectSP(new StructuredData::Boolean(true));503} else if (element_name == "false") {504return StructuredData::ObjectSP(new StructuredData::Boolean(false));505}506return StructuredData::ObjectSP(new StructuredData::Null());507}508#endif509510StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {511StructuredData::ObjectSP root_sp;512#if LLDB_ENABLE_LIBXML2513if (IsValid()) {514return CreatePlistValue(m_dict_node);515}516#endif517return root_sp;518}519520521