1 //===-- XML.cpp -------------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <stdlib.h> /* atof */
10 
11 #include "lldb/Host/StringConvert.h"
12 #include "lldb/Host/XML.h"
13 
14 using namespace lldb;
15 using namespace lldb_private;
16 
17 #pragma mark-- XMLDocument
18 
19 XMLDocument::XMLDocument() : m_document(nullptr) {}
20 
21 XMLDocument::~XMLDocument() { Clear(); }
22 
23 void XMLDocument::Clear() {
24 #if defined(LIBXML2_DEFINED)
25   if (m_document) {
26     xmlDocPtr doc = m_document;
27     m_document = nullptr;
28     xmlFreeDoc(doc);
29   }
30 #endif
31 }
32 
33 bool XMLDocument::IsValid() const { return m_document != nullptr; }
34 
35 void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
36   XMLDocument *document = (XMLDocument *)ctx;
37   va_list args;
38   va_start(args, format);
39   document->m_errors.PrintfVarArg(format, args);
40   document->m_errors.EOL();
41   va_end(args);
42 }
43 
44 bool XMLDocument::ParseFile(const char *path) {
45 #if defined(LIBXML2_DEFINED)
46   Clear();
47   xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
48   m_document = xmlParseFile(path);
49   xmlSetGenericErrorFunc(nullptr, nullptr);
50 #endif
51   return IsValid();
52 }
53 
54 bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
55                               const char *url) {
56 #if defined(LIBXML2_DEFINED)
57   Clear();
58   xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
59   m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
60   xmlSetGenericErrorFunc(nullptr, nullptr);
61 #endif
62   return IsValid();
63 }
64 
65 XMLNode XMLDocument::GetRootElement(const char *required_name) {
66 #if defined(LIBXML2_DEFINED)
67   if (IsValid()) {
68     XMLNode root_node(xmlDocGetRootElement(m_document));
69     if (required_name) {
70       llvm::StringRef actual_name = root_node.GetName();
71       if (actual_name == required_name)
72         return root_node;
73     } else {
74       return root_node;
75     }
76   }
77 #endif
78   return XMLNode();
79 }
80 
81 llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
82 
83 bool XMLDocument::XMLEnabled() {
84 #if defined(LIBXML2_DEFINED)
85   return true;
86 #else
87   return false;
88 #endif
89 }
90 
91 #pragma mark-- XMLNode
92 
93 XMLNode::XMLNode() : m_node(nullptr) {}
94 
95 XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
96 
97 XMLNode::~XMLNode() {}
98 
99 void XMLNode::Clear() { m_node = nullptr; }
100 
101 XMLNode XMLNode::GetParent() const {
102 #if defined(LIBXML2_DEFINED)
103   if (IsValid())
104     return XMLNode(m_node->parent);
105   else
106     return XMLNode();
107 #else
108   return XMLNode();
109 #endif
110 }
111 
112 XMLNode XMLNode::GetSibling() const {
113 #if defined(LIBXML2_DEFINED)
114   if (IsValid())
115     return XMLNode(m_node->next);
116   else
117     return XMLNode();
118 #else
119   return XMLNode();
120 #endif
121 }
122 
123 XMLNode XMLNode::GetChild() const {
124 #if defined(LIBXML2_DEFINED)
125 
126   if (IsValid())
127     return XMLNode(m_node->children);
128   else
129     return XMLNode();
130 #else
131   return XMLNode();
132 #endif
133 }
134 
135 llvm::StringRef XMLNode::GetAttributeValue(const char *name,
136                                            const char *fail_value) const {
137   const char *attr_value = nullptr;
138 #if defined(LIBXML2_DEFINED)
139 
140   if (IsValid())
141     attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name);
142   else
143     attr_value = fail_value;
144 #else
145   attr_value = fail_value;
146 #endif
147   if (attr_value)
148     return llvm::StringRef(attr_value);
149   else
150     return llvm::StringRef();
151 }
152 
153 bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
154                                           uint64_t fail_value, int base) const {
155 #if defined(LIBXML2_DEFINED)
156   llvm::StringRef str_value = GetAttributeValue(name, "");
157 #else
158   llvm::StringRef str_value;
159 #endif
160   bool success = false;
161   value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success);
162   return success;
163 }
164 
165 void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
166 #if defined(LIBXML2_DEFINED)
167   if (IsValid())
168     GetChild().ForEachSiblingNode(callback);
169 #endif
170 }
171 
172 void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
173 #if defined(LIBXML2_DEFINED)
174   XMLNode child = GetChild();
175   if (child)
176     child.ForEachSiblingElement(callback);
177 #endif
178 }
179 
180 void XMLNode::ForEachChildElementWithName(const char *name,
181                                           NodeCallback const &callback) const {
182 #if defined(LIBXML2_DEFINED)
183   XMLNode child = GetChild();
184   if (child)
185     child.ForEachSiblingElementWithName(name, callback);
186 #endif
187 }
188 
189 void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
190 #if defined(LIBXML2_DEFINED)
191 
192   if (IsValid()) {
193     for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
194          attr = attr->next) {
195       // check if name matches
196       if (attr->name) {
197         // check child is a text node
198         xmlNodePtr child = attr->children;
199         if (child->type == XML_TEXT_NODE) {
200           llvm::StringRef attr_value;
201           if (child->content)
202             attr_value = llvm::StringRef((const char *)child->content);
203           if (!callback(llvm::StringRef((const char *)attr->name), attr_value))
204             return;
205         }
206       }
207     }
208   }
209 #endif
210 }
211 
212 void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
213 #if defined(LIBXML2_DEFINED)
214 
215   if (IsValid()) {
216     // iterate through all siblings
217     for (xmlNodePtr node = m_node; node; node = node->next) {
218       if (!callback(XMLNode(node)))
219         return;
220     }
221   }
222 #endif
223 }
224 
225 void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
226 #if defined(LIBXML2_DEFINED)
227 
228   if (IsValid()) {
229     // iterate through all siblings
230     for (xmlNodePtr node = m_node; node; node = node->next) {
231       // we are looking for element nodes only
232       if (node->type != XML_ELEMENT_NODE)
233         continue;
234 
235       if (!callback(XMLNode(node)))
236         return;
237     }
238   }
239 #endif
240 }
241 
242 void XMLNode::ForEachSiblingElementWithName(
243     const char *name, NodeCallback const &callback) const {
244 #if defined(LIBXML2_DEFINED)
245 
246   if (IsValid()) {
247     // iterate through all siblings
248     for (xmlNodePtr node = m_node; node; node = node->next) {
249       // we are looking for element nodes only
250       if (node->type != XML_ELEMENT_NODE)
251         continue;
252 
253       // If name is nullptr, we take all nodes of type "t", else just the ones
254       // whose name matches
255       if (name) {
256         if (strcmp((const char *)node->name, name) != 0)
257           continue; // Name mismatch, ignore this one
258       } else {
259         if (node->name)
260           continue; // nullptr name specified and this element has a name,
261                     // ignore this one
262       }
263 
264       if (!callback(XMLNode(node)))
265         return;
266     }
267   }
268 #endif
269 }
270 
271 llvm::StringRef XMLNode::GetName() const {
272 #if defined(LIBXML2_DEFINED)
273   if (IsValid()) {
274     if (m_node->name)
275       return llvm::StringRef((const char *)m_node->name);
276   }
277 #endif
278   return llvm::StringRef();
279 }
280 
281 bool XMLNode::GetElementText(std::string &text) const {
282   text.clear();
283 #if defined(LIBXML2_DEFINED)
284   if (IsValid()) {
285     bool success = false;
286     if (m_node->type == XML_ELEMENT_NODE) {
287       // check child is a text node
288       for (xmlNodePtr node = m_node->children; node != nullptr;
289            node = node->next) {
290         if (node->type == XML_TEXT_NODE) {
291           text.append((const char *)node->content);
292           success = true;
293         }
294       }
295     }
296     return success;
297   }
298 #endif
299   return false;
300 }
301 
302 bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
303                                        int base) const {
304   bool success = false;
305 #if defined(LIBXML2_DEFINED)
306   if (IsValid()) {
307     std::string text;
308     if (GetElementText(text))
309       value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success);
310   }
311 #endif
312   if (!success)
313     value = fail_value;
314   return success;
315 }
316 
317 bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
318   bool success = false;
319 #if defined(LIBXML2_DEFINED)
320   if (IsValid()) {
321     std::string text;
322     if (GetElementText(text)) {
323       value = atof(text.c_str());
324       success = true;
325     }
326   }
327 #endif
328   if (!success)
329     value = fail_value;
330   return success;
331 }
332 
333 bool XMLNode::NameIs(const char *name) const {
334 #if defined(LIBXML2_DEFINED)
335 
336   if (IsValid()) {
337     // In case we are looking for a nullptr name or an exact pointer match
338     if (m_node->name == (const xmlChar *)name)
339       return true;
340     if (m_node->name)
341       return strcmp((const char *)m_node->name, name) == 0;
342   }
343 #endif
344   return false;
345 }
346 
347 XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
348   XMLNode result_node;
349 
350 #if defined(LIBXML2_DEFINED)
351   ForEachChildElementWithName(
352       name, [&result_node](const XMLNode &node) -> bool {
353         result_node = node;
354         // Stop iterating, we found the node we wanted
355         return false;
356       });
357 #endif
358 
359   return result_node;
360 }
361 
362 bool XMLNode::IsValid() const { return m_node != nullptr; }
363 
364 bool XMLNode::IsElement() const {
365 #if defined(LIBXML2_DEFINED)
366   if (IsValid())
367     return m_node->type == XML_ELEMENT_NODE;
368 #endif
369   return false;
370 }
371 
372 XMLNode XMLNode::GetElementForPath(const NamePath &path) {
373 #if defined(LIBXML2_DEFINED)
374 
375   if (IsValid()) {
376     if (path.empty())
377       return *this;
378     else {
379       XMLNode node = FindFirstChildElementWithName(path[0].c_str());
380       const size_t n = path.size();
381       for (size_t i = 1; node && i < n; ++i)
382         node = node.FindFirstChildElementWithName(path[i].c_str());
383       return node;
384     }
385   }
386 #endif
387 
388   return XMLNode();
389 }
390 
391 #pragma mark-- ApplePropertyList
392 
393 ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
394 
395 ApplePropertyList::ApplePropertyList(const char *path)
396     : m_xml_doc(), m_dict_node() {
397   ParseFile(path);
398 }
399 
400 ApplePropertyList::~ApplePropertyList() {}
401 
402 llvm::StringRef ApplePropertyList::GetErrors() const {
403   return m_xml_doc.GetErrors();
404 }
405 
406 bool ApplePropertyList::ParseFile(const char *path) {
407   if (m_xml_doc.ParseFile(path)) {
408     XMLNode plist = m_xml_doc.GetRootElement("plist");
409     if (plist) {
410       plist.ForEachChildElementWithName("dict",
411                                         [this](const XMLNode &dict) -> bool {
412                                           this->m_dict_node = dict;
413                                           return false; // Stop iterating
414                                         });
415       return (bool)m_dict_node;
416     }
417   }
418   return false;
419 }
420 
421 bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
422 
423 bool ApplePropertyList::GetValueAsString(const char *key,
424                                          std::string &value) const {
425   XMLNode value_node = GetValueNode(key);
426   if (value_node)
427     return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
428   return false;
429 }
430 
431 XMLNode ApplePropertyList::GetValueNode(const char *key) const {
432   XMLNode value_node;
433 #if defined(LIBXML2_DEFINED)
434 
435   if (IsValid()) {
436     m_dict_node.ForEachChildElementWithName(
437         "key", [key, &value_node](const XMLNode &key_node) -> bool {
438           std::string key_name;
439           if (key_node.GetElementText(key_name)) {
440             if (key_name == key) {
441               value_node = key_node.GetSibling();
442               while (value_node && !value_node.IsElement())
443                 value_node = value_node.GetSibling();
444               return false; // Stop iterating
445             }
446           }
447           return true; // Keep iterating
448         });
449   }
450 #endif
451   return value_node;
452 }
453 
454 bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
455                                                    std::string &value) {
456   value.clear();
457 #if defined(LIBXML2_DEFINED)
458   if (node.IsValid()) {
459     llvm::StringRef element_name = node.GetName();
460     if (element_name == "true" || element_name == "false") {
461       // The text value _is_ the element name itself...
462       value = element_name.str();
463       return true;
464     } else if (element_name == "dict" || element_name == "array")
465       return false; // dictionaries and arrays have no text value, so we fail
466     else
467       return node.GetElementText(value);
468   }
469 #endif
470   return false;
471 }
472 
473 #if defined(LIBXML2_DEFINED)
474 
475 namespace {
476 
477 StructuredData::ObjectSP CreatePlistValue(XMLNode node) {
478   llvm::StringRef element_name = node.GetName();
479   if (element_name == "array") {
480     std::shared_ptr<StructuredData::Array> array_sp(
481         new StructuredData::Array());
482     node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
483       array_sp->AddItem(CreatePlistValue(node));
484       return true; // Keep iterating through all child elements of the array
485     });
486     return array_sp;
487   } else if (element_name == "dict") {
488     XMLNode key_node;
489     std::shared_ptr<StructuredData::Dictionary> dict_sp(
490         new StructuredData::Dictionary());
491     node.ForEachChildElement(
492         [&key_node, &dict_sp](const XMLNode &node) -> bool {
493           if (node.NameIs("key")) {
494             // This is a "key" element node
495             key_node = node;
496           } else {
497             // This is a value node
498             if (key_node) {
499               std::string key_name;
500               key_node.GetElementText(key_name);
501               dict_sp->AddItem(key_name, CreatePlistValue(node));
502               key_node.Clear();
503             }
504           }
505           return true; // Keep iterating through all child elements of the
506                        // dictionary
507         });
508     return dict_sp;
509   } else if (element_name == "real") {
510     double value = 0.0;
511     node.GetElementTextAsFloat(value);
512     return StructuredData::ObjectSP(new StructuredData::Float(value));
513   } else if (element_name == "integer") {
514     uint64_t value = 0;
515     node.GetElementTextAsUnsigned(value, 0, 0);
516     return StructuredData::ObjectSP(new StructuredData::Integer(value));
517   } else if ((element_name == "string") || (element_name == "data") ||
518              (element_name == "date")) {
519     std::string text;
520     node.GetElementText(text);
521     return StructuredData::ObjectSP(
522         new StructuredData::String(std::move(text)));
523   } else if (element_name == "true") {
524     return StructuredData::ObjectSP(new StructuredData::Boolean(true));
525   } else if (element_name == "false") {
526     return StructuredData::ObjectSP(new StructuredData::Boolean(false));
527   }
528   return StructuredData::ObjectSP(new StructuredData::Null());
529 }
530 }
531 #endif
532 
533 StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
534   StructuredData::ObjectSP root_sp;
535 #if defined(LIBXML2_DEFINED)
536   if (IsValid()) {
537     return CreatePlistValue(m_dict_node);
538   }
539 #endif
540   return root_sp;
541 }
542