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