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