libabigail
Loading...
Searching...
No Matches
abg-libxml-utils.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2// -*- mode: C++ -*-
3//
4// Copyright (C) 2013-2026 Red Hat, Inc.
5
6/// @file
7
8#include <string>
9#include <iostream>
10#include <fstream>
11#include "abg-tools-utils.h"
12
13#include "abg-internal.h"
14// <headers defining libabigail's API go under here>
15ABG_BEGIN_EXPORT_DECLARATIONS
16
17#include "abg-libxml-utils.h"
18
19ABG_END_EXPORT_DECLARATIONS
20// </headers defining libabigail's API>
21
22namespace abigail
23{
24
25namespace sptr_utils
26{
27/// Build and return a shared_ptr for a pointer to xmlTextReader
28template<>
29shared_ptr<xmlTextReader>
30build_sptr<xmlTextReader>(::xmlTextReader *p)
31{
32 return shared_ptr<xmlTextReader>(p, abigail::xml::textReaderDeleter());
33}
34
35/// Build and return a shared_ptr for a pointer to xmlChar
36template<>
37shared_ptr<xmlChar>
38build_sptr<xmlChar>(xmlChar *p)
39{
40 return shared_ptr<xmlChar>(p, abigail::xml::charDeleter());
41}
42
43}//end namespace sptr_utils
44
45namespace xml
46{
47using std::istream;
48using std::ifstream;
52
53// <xmlIO callbacks for xz reading support>
54
55
56/// This is an xmlIO callback function used in the libxml2 I/O input
57/// API to detect if the current handler can provider input
58/// functionality for a file designed by a path.
59///
60/// This function should return 1 iff the file contains XZ-compressed
61/// data.
62///
63/// @param filepath the path to file to consider.
64///
65/// @return 1 iff the file designated by @p filepath is XZ-compressed.
66static int
67xz_io_match_cb(const char*filepath)
68{
69 bool does_match = false;
70 file_type t = guess_file_type(filepath, /*look_through_compression=*/false);
72 does_match = true;
73
74 return does_match;
75}
76
77/// This is the context used by the xmlIO handler that provides input
78/// functionality to the libxml2 I/O input API for XZ-compressed XML
79/// files.
80struct xz_ctxt_type
81{
82 // The input XZ-compressed file stream.
83 std::unique_ptr<std::ifstream> input_fstream;
84 // The custom XZ-decompressor streambuf provided by tools-utils.
85 std::unique_ptr<xz_decompressor_type> decompressor_streambuf;
86 // The decompressed input stream that we can read from.
87 std::unique_ptr<std::istream> decompressed_input_stream;
88
89 xz_ctxt_type() = delete;
90
91 /// Constructor.
92 ///
93 /// @param is the XZ-compressed input file stream to consider.
94 xz_ctxt_type(std::ifstream* is)
95 : input_fstream(is),
96 decompressor_streambuf(new xz_decompressor_type(*is)),
97 decompressed_input_stream(new istream(decompressor_streambuf.get()))
98 {}
99}; // end struct xz_ctxt_type.
100
101/// Callback used in the I/O input API of libxml2 to open a file
102/// designated by a path and containing XZ-compressed content.
103///
104/// @param filepath the path to the file to open. The file should
105/// contain XZ-compressed data, as detected by @ref xz_io_match_cb.
106///
107/// @return a pointer to an instance of @ref xz_ctxt_type if the
108/// function could successfully open the file denoted by @p filepath.
109/// Please note that this instance of @ref xz_ctxt_type has to be
110/// deleted by @ref xz_io_close_cb.
111static void*
112xz_io_open_cb(const char* filepath)
113{
114 std::ifstream* s = new std::ifstream(filepath, ifstream::binary);
115 if (s->bad())
116 {
117 delete s;
118 return nullptr;
119 }
120
121 xz_ctxt_type *ctxt = new xz_ctxt_type(s);
122 return ctxt;
123}
124
125/// Callback used in the I/O input API of libxml2 to read and
126/// decompress data from an XZ-compressed file previously opened by
127/// @ref xz_io_open_cb.
128///
129/// @param context a pointer to the instance of @ref xz_ctxt_type
130/// returned by @ref xz_io_open_cb. That context is used to read and
131/// decompress the XZ-compressed data coming from input file.
132///
133/// @param buffer the buffer where to copy the XZ-decompressed data.
134///
135/// @param len the length of @p buffer.
136///
137/// @return the actual number of bytes decompressed and copied into @p
138/// buffer.
139static int
140xz_io_read_cb(void* context, char *buffer, int len)
141{
142 xz_ctxt_type *ctxt = static_cast<xz_ctxt_type *>(context);
143 ctxt->decompressed_input_stream->read(buffer, len);
144 int nb_bytes_read = ctxt->decompressed_input_stream->gcount();
145 return nb_bytes_read;
146}
147
148/// Callback used in the I/O input API of libxml2 to delete the
149/// instance of @ref xz_ctxt_type created by @ref xz_io_open_cb and
150/// free its associated resources.
151///
152/// @param context the pointer to the instance of @ref xz_ctxt_type to
153/// delete.
154///
155/// @return 0 iff the operation was successful.
156static int
157xz_io_close_cb(void* context)
158{
159 xz_ctxt_type *ctxt = static_cast<xz_ctxt_type*>(context);
160 ctxt->decompressed_input_stream.reset();
161 ctxt->input_fstream->close();
162 ctxt->input_fstream.reset();
163 delete ctxt;
164 return 0;
165}
166
167// </xmlIO callbacks for xz reading support>
168
169/// The initialization function of libxml2 abstraction layer. This
170/// function must be called prior to using any of the libxml2 capabilities.
171void
173{
174 LIBXML_TEST_VERSION;
175 xmlInitParser();
176 xmlRegisterInputCallbacks(xz_io_match_cb, xz_io_open_cb,
177 xz_io_read_cb, xz_io_close_cb);
178}
179
180/// Instantiate an xmlTextReader that parses the content of an on-disk
181/// file, wrap it into a smart pointer and return it.
182///
183/// @param path the path to the file to be parsed by the returned
184/// instance of xmlTextReader.
186new_reader_from_file(const std::string& path)
187{
188 reader_sptr p =
189 build_sptr(xmlNewTextReaderFilename (path.c_str()));
190 xmlTextReaderSetParserProp(p.get(), XML_PARSE_BIG_LINES, true);
191
192 return p;
193}
194
195/// Instanciate an xmlTextReader that parses the content of an
196/// in-memory buffer, wrap it into a smart pointer and return it.
197///
198/// @param buffer the in-memory buffer to be parsed by the returned
199/// instance of xmlTextReader.
201new_reader_from_buffer(const std::string& buffer)
202{
203 reader_sptr p =
204 build_sptr(xmlReaderForMemory(buffer.c_str(),
205 buffer.length(),
206 "", 0, 0));
207 xmlTextReaderSetParserProp(p.get(), XML_PARSE_BIG_LINES, true);
208 return p;
209}
210
211/// This is an xmlInputReadCallback, meant to be passed to
212/// xmlNewTextReaderForIO. It reads a number of bytes from an istream.
213///
214/// @param context an std::istream* cast into a void*. This is the
215/// istream that the xmlTextReader is too read data from.
216///
217/// @param buffer the buffer where to copy the data read from the
218/// input stream.
219///
220/// @param len the number of byte to read from the input stream and to
221/// copy into @p buffer.
222///
223/// @return the number of bytes read or -1 in case of error.
224static int
225xml_istream_input_read(void* context,
226 char* buffer,
227 int len)
228{
229 istream* in = reinterpret_cast<istream*>(context);
230 in->read(buffer, len);
231 return in->gcount();
232}
233
234/// This is an xmlInputCloseCallback, meant to be passed to
235/// xmlNewTextReaderForIO. It's supposed to close the input stream
236/// that the xmlTextReader is reading from. This particular
237/// implementation is noop; it does nothing.
238///
239/// @return 0.
240static int
241xml_istream_input_close(void*)
242{return 0;}
243
244/// Instanciate an xmlTextReader that parses a content coming from an
245/// input stream.
246///
247/// @param in the input stream to consider.
248///
249/// @return reader_sptr a pointer to the newly instantiated xml
250/// reader.
252new_reader_from_istream(std::istream* in)
253{
254 reader_sptr p =
255 build_sptr(xmlReaderForIO(&xml_istream_input_read,
256 &xml_istream_input_close,
257 in, "", 0, 0));
258 xmlTextReaderSetParserProp(p.get(), XML_PARSE_BIG_LINES, true);
259
260 return p;
261}
262
263/// Convert a shared pointer to xmlChar into an std::string.
264///
265/// If the xmlChar is NULL, set "" to the string.
266///
267/// @param ssptr the shared point to xmlChar to convert.
268///
269/// @param s the output string.
270///
271/// @return true if the shared pointer to xmlChar contained a non NULL
272/// string, false otherwise.
273bool
275{
276 bool non_nil = false;
277 if (CHAR_STR(ssptr))
278 {
279 s = CHAR_STR(ssptr);
280 non_nil = true;
281 }
282 else
283 {
284 s = "";
285 non_nil = false;
286 }
287
288 return non_nil;
289}
290
291/// Return the depth of an xml element node.
292///
293/// Note that the node must be attached to an XML document.
294///
295/// @param n the xml to consider.
296///
297/// @return a positive or zero number for an XML node properly
298/// attached to an xml document, -1 otherwise. Note that the function
299/// returns -1 if passed an xml document as well.
300int
302{
303 if (n->type == XML_DOCUMENT_NODE || n->parent == NULL)
304 return -1;
305
306 if (n->parent->type == XML_DOCUMENT_NODE)
307 return 0;
308
309 return 1 + get_xml_node_depth(n->parent);
310}
311
312/// Escape the 5 characters representing the predefined XML entities.
313///
314/// The resulting entities and their matching characters are:
315///
316/// &lt; for the character '<', &gt; for the character '>', &apos; for
317/// the character ''', &quot; for the character '"', and &amp; for the
318/// character '&'.
319///
320//// @param str the input string to read to search for the characters
321//// to escape.
322////
323//// @param escaped the output string where to write the resulting
324//// string that contains the pre-defined characters escaped as
325//// predefined entitites.
326void
327escape_xml_string(const std::string& str,
328 std::string& escaped)
329{
330 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
331 switch (*i)
332 {
333 case '<':
334 escaped += "&lt;";
335 break;
336 case '>':
337 escaped += "&gt;";
338 break;
339 case '&':
340 escaped += "&amp;";
341 break;
342 case '\'':
343 escaped += "&apos;";
344 break;
345 case '"':
346 escaped += "&quot;";
347 break;
348 default:
349 escaped += *i;
350 }
351}
352
353/// Escape the 5 characters representing the predefined XML entities.
354///
355/// The resulting entities and their matching characters are:
356///
357/// &lt; for the character '<', &gt; for the character '>', &apos; for
358/// the character ''', &quot; for the character '"', and &amp; for the
359/// character '&'.
360///
361//// @param str the input string to read to search for the characters
362//// to escape.
363////
364//// @return the resulting string that contains the pre-defined
365//// characters escaped as predefined entitites.
366std::string
367escape_xml_string(const std::string& str)
368{
369 std::string result;
370 escape_xml_string(str, result);
371 return result;
372}
373
374/// Escape the '-' character, to avoid having a '--' in a comment.
375///
376/// The resulting entity for '-' is '&#45;'.
377///
378//// @param str the input string to read to search for the characters
379//// to escape.
380////
381//// @param escaped the output string where to write the resulting
382//// string that contains the pre-defined characters escaped as
383//// predefined entitites.
384void
385escape_xml_comment(const std::string& str,
386 std::string& escaped)
387{
388 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
389 switch (*i)
390 {
391 case '-':
392 escaped += "&#45;";
393 break;
394 default:
395 escaped += *i;
396 }
397}
398
399/// Escape the '-' character, to avoid having a '--' in a comment.
400///
401/// The resulting entity for '-' is '&#45;'.
402///
403//// @param str the input string to read to search for the characters
404//// to escape.
405////
406//// @return the resulting string that contains the pre-defined
407//// characters escaped as predefined entitites.
408std::string
409escape_xml_comment(const std::string& str)
410{
411 std::string result;
412 escape_xml_comment(str, result);
413 return result;
414}
415
416/// Read a string, detect the 5 predefined XML entities it may contain
417/// and un-escape them, by writting their corresponding characters
418/// back in. The pre-defined entities are:
419///
420/// &lt; for the character '<', &gt; for the character '>', &apos; for
421/// the character ''', &quot; for the character '"', and &amp; for the
422/// character '&'.
423///
424/// @param str the input XML string to consider.
425///
426/// @param escaped where to write the resulting un-escaped string.
427void
428unescape_xml_string(const std::string& str,
429 std::string& escaped)
430{
431 std::string::size_type i = 0;
432 while (i < str.size())
433 {
434 if (str[i] == '&')
435 {
436 if (str[i+1] == 'l'
437 && str[i+2] == 't'
438 && str[i+3] == ';')
439 {
440 escaped += '<';
441 i+= 4;
442 }
443 else if (str[i+1] == 'g'
444 && str[i+2] == 't'
445 && str[i+3] == ';')
446 {
447 escaped += '>';
448 i += 4;
449 }
450 else if (str[i+1] == 'a'
451 && str[i+2] == 'm'
452 && str[i+3] == 'p'
453 && str[i+4] == ';')
454 {
455 escaped += '&';
456 i += 5;
457 }
458 else if (str[i+1] == 'a'
459 && str[i+2] == 'p'
460 && str[i+3] == 'o'
461 && str[i+4] == 's'
462 && str[i+5] == ';')
463 {
464 escaped += '\'';
465 i += 6;
466 }
467 else if (str[i+1] == 'q'
468 && str[i+2] == 'u'
469 && str[i+3] == 'o'
470 && str[i+4] == 't'
471 && str[i+5] == ';')
472 {
473 escaped += '"';
474 i += 6;
475 }
476 else
477 {
478 escaped += str[i];
479 ++i;
480 }
481 }
482 else
483 {
484 escaped += str[i];
485 ++i;
486 }
487 }
488}
489
490/// Read a string, detect the 5 predefined XML entities it may contain
491/// and un-escape them, by writting their corresponding characters
492/// back in. The pre-defined entities are:
493///
494/// &lt; for the character '<', &gt; for the character '>', &apos; for
495/// the character ''', &quot; for the character '"', and &amp; for the
496/// character '&'.
497///
498/// @param str the input XML string to consider.
499///
500/// @return escaped where to write the resulting un-escaped string.
501std::string
502unescape_xml_string(const std::string& str)
503{
504 std::string result;
505 unescape_xml_string(str, result);
506 return result;
507}
508
509/// Read a string, detect the '#&45;' entity and un-escape it into
510/// the '-' character.
511///
512/// @param str the input XML string to consider.
513///
514/// @param escaped where to write the resulting un-escaped string.
515void
516unescape_xml_comment(const std::string& str,
517 std::string& escaped)
518{
519 std::string::size_type i = 0;
520 while (i < str.size())
521 {
522 if (str[i] == '&'
523 && str[i + 1] == '#'
524 && str[i + 2] == '4'
525 && str[i + 3] == '5'
526 && str[i + 4] == ';')
527 {
528 escaped += '-';
529 i += 5;
530 }
531 else
532 {
533 escaped += str[i];
534 ++i;
535 }
536 }
537}
538
539/// Read a string, detect the '#&45;' entity and un-escape it into
540/// the '-' character.
541///
542/// @param str the input XML string to consider.
543///
544/// @return escaped where to write the resulting un-escaped string.
545std::string
546unescape_xml_comment(const std::string& str)
547{
548 std::string result;
549 unescape_xml_comment(str, result);
550 return result;
551}
552
553}//end namespace xml
554}//end namespace abigail
This is a custom std::streambuf that knows how to decompress an input stream that was compressed usin...
shared_ptr< xmlChar > build_sptr< xmlChar >(xmlChar *p)
Build and return a shared_ptr for a pointer to xmlChar.
shared_ptr< xmlTextReader > build_sptr< xmlTextReader >(::xmlTextReader *p)
Build and return a shared_ptr for a pointer to xmlTextReader.
file_type
The different types of files understood the bi* suite of tools.
@ FILE_TYPE_XZ
The XZ (lzma) compresson scheme.
file_type guess_file_type(istream &in)
Guess the type of the content of an input stream.
void unescape_xml_comment(const std::string &str, std::string &escaped)
Read a string, detect the '#&45;' entity and un-escape it into the '-' character.
reader_sptr new_reader_from_file(const std::string &path)
Instantiate an xmlTextReader that parses the content of an on-disk file, wrap it into a smart pointer...
void initialize()
The initialization function of libxml2 abstraction layer. This function must be called prior to using...
int get_xml_node_depth(xmlNodePtr n)
Return the depth of an xml element node.
void escape_xml_comment(const std::string &str, std::string &escaped)
Escape the '-' character, to avoid having a '–' in a comment.
bool xml_char_sptr_to_string(xml_char_sptr &ssptr, std::string &s)
Convert a shared pointer to xmlChar into an std::string.
reader_sptr new_reader_from_buffer(const std::string &buffer)
Instanciate an xmlTextReader that parses the content of an in-memory buffer, wrap it into a smart poi...
shared_ptr< xmlChar > xml_char_sptr
A convenience typedef for a shared pointer of xmlChar.
void unescape_xml_string(const std::string &str, std::string &escaped)
Read a string, detect the 5 predefined XML entities it may contain and un-escape them,...
reader_sptr new_reader_from_istream(std::istream *in)
Instanciate an xmlTextReader that parses a content coming from an input stream.
shared_ptr< xmlTextReader > reader_sptr
A convenience typedef for a shared pointer of xmlTextReader.
void escape_xml_string(const std::string &str, std::string &escaped)
Escape the 5 characters representing the predefined XML entities.
Toplevel namespace for libabigail.
This functor is used to instantiate a shared_ptr for xmlChar.
This functor is used to instantiate a shared_ptr for the xmlTextReader.