2011-12-22 15:19:25 +00:00
|
|
|
/*
|
|
|
|
* \brief XML parser
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2007-08-21
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 12:23:52 +00:00
|
|
|
* Copyright (C) 2007-2017 Genode Labs GmbH
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 12:23:52 +00:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _INCLUDE__UTIL__XML_NODE_H_
|
|
|
|
#define _INCLUDE__UTIL__XML_NODE_H_
|
|
|
|
|
|
|
|
#include <util/token.h>
|
|
|
|
#include <base/exception.h>
|
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
namespace Genode {
|
|
|
|
class Xml_attribute;
|
|
|
|
class Xml_node;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
|
|
|
|
/**
|
2015-03-22 02:26:28 +00:00
|
|
|
* Representation of an XML-node attribute
|
|
|
|
*
|
|
|
|
* An attribute has the form 'name="value"'.
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
2015-03-22 02:26:28 +00:00
|
|
|
class Genode::Xml_attribute
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
2015-03-22 02:26:28 +00:00
|
|
|
private:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scanner policy that accepts hyphens in identifiers
|
|
|
|
*/
|
|
|
|
struct Scanner_policy_xml_identifier {
|
|
|
|
static bool identifier_char(char c, unsigned i) {
|
|
|
|
return is_letter(c) || c == '_' || c == ':'
|
|
|
|
|| (i && (c == '-' || c == '.' || is_digit(c))); } };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Define tokenizer that matches XML tags (with hyphens) as identifiers
|
|
|
|
*/
|
|
|
|
typedef ::Genode::Token<Scanner_policy_xml_identifier> Token;
|
|
|
|
|
|
|
|
Token _name;
|
|
|
|
Token _value;
|
|
|
|
|
|
|
|
friend class Xml_node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Even though 'Tag' is part of 'Xml_node', the friendship
|
|
|
|
* to 'Xml_node' does not apply for 'Tag' when compiling
|
|
|
|
* the code with 'gcc-3.4'. Hence, we need to add an
|
|
|
|
* explicit friendship to 'Tag'.
|
|
|
|
*/
|
|
|
|
friend class Tag;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* This constructor is meant to be used as implicitly to
|
|
|
|
* construct an 'Xml_attribute' from a token sequence via an
|
|
|
|
* assignment from the leading 'Token'.
|
|
|
|
*/
|
|
|
|
Xml_attribute(Token t) :
|
|
|
|
_name(t.eat_whitespace()), _value(_name.next().next())
|
|
|
|
{
|
|
|
|
if (_name.type() != Token::IDENT)
|
|
|
|
throw Nonexistent_attribute();
|
|
|
|
|
|
|
|
if (_name.next()[0] != '=' || _value.type() != Token::STRING)
|
|
|
|
throw Invalid_syntax();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return token following the attribute declaration
|
|
|
|
*/
|
|
|
|
Token _next() const { return _name.next().next().next(); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
public:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/*********************
|
|
|
|
** Exception types **
|
|
|
|
*********************/
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
class Invalid_syntax : public Exception { };
|
|
|
|
class Nonexistent_attribute : public Exception { };
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2015-03-22 02:26:28 +00:00
|
|
|
* Return attribute type as null-terminated string
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
2015-03-22 02:26:28 +00:00
|
|
|
void type(char *dst, size_t max_len) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
2015-03-22 02:26:28 +00:00
|
|
|
/*
|
|
|
|
* Limit number of characters by token length, take
|
|
|
|
* null-termination into account.
|
|
|
|
*/
|
|
|
|
max_len = min(max_len, _name.len() + 1);
|
|
|
|
strncpy(dst, _name.start(), max_len);
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-11-23 12:34:17 +00:00
|
|
|
typedef String<64> Name;
|
|
|
|
Name name() const {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
return Name(Cstring(_name.start(), _name.len())); }
|
2015-11-23 12:34:17 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Return true if attribute has specified type
|
|
|
|
*/
|
|
|
|
bool has_type(const char *type) {
|
|
|
|
return strlen(type) == _name.len() &&
|
|
|
|
strcmp(type, _name.start(), _name.len()) == 0; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Return size of value
|
2019-01-21 09:33:23 +00:00
|
|
|
*
|
|
|
|
* \deprecated use 'with_raw_node' instead
|
2015-03-22 02:26:28 +00:00
|
|
|
*/
|
|
|
|
char const *value_base() const { return _value.start() + 1; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Return size of the value in bytes
|
2015-03-22 02:26:28 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
size_t value_size() const
|
2015-03-22 02:26:28 +00:00
|
|
|
{
|
|
|
|
/*
|
2019-01-21 09:33:23 +00:00
|
|
|
* The size of the actual value content excludes both the starting
|
|
|
|
* and the trailing quote character.
|
|
|
|
*
|
|
|
|
* The invariant 'len >= 2' is enforced by the 'Xml_attribute'
|
|
|
|
* constructor by checking the '_value' type for being a 'STRING'.
|
2015-03-22 02:26:28 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
return _value.len() - 2;
|
2015-03-22 02:26:28 +00:00
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Return true if attribute has the specified value
|
|
|
|
*/
|
|
|
|
bool has_value(const char *value) const {
|
2019-01-21 09:33:23 +00:00
|
|
|
return strlen(value) == (_value.len() - 2)
|
|
|
|
&& !strcmp(value, _value.start() + 1, _value.len() - 2); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call functor 'fn' with the data of the attribute value as argument
|
|
|
|
*
|
|
|
|
* The functor is called with the start pointer ('char const *') and
|
|
|
|
* size (size_t) of the attribute value as arguments.
|
|
|
|
*
|
|
|
|
* Note that the content of the buffer is not null-terminated but
|
|
|
|
* delimited by the size argument.
|
|
|
|
*/
|
|
|
|
template <typename FN>
|
|
|
|
void with_raw_value(FN const &fn) const
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Skip leading quote of the '_value' to access the actual value.
|
|
|
|
*/
|
|
|
|
fn(_value.start() + 1, value_size());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return attribute value as null-terminated string
|
|
|
|
*
|
|
|
|
* \deprecated
|
|
|
|
*/
|
|
|
|
void value(char *dst, size_t max_len) const
|
|
|
|
{
|
|
|
|
with_raw_value([&] (char const *start, size_t length) {
|
|
|
|
Genode::strncpy(dst, start, min(max_len, length + 1)); });
|
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Return attribute value as typed value
|
|
|
|
*
|
|
|
|
* \param T type of value to read
|
|
|
|
* \return true on success, or
|
|
|
|
* false if attribute is invalid or value
|
|
|
|
* conversion failed
|
|
|
|
*/
|
|
|
|
template <typename T>
|
2019-01-21 08:34:42 +00:00
|
|
|
bool value(T &out) const
|
2015-03-22 02:26:28 +00:00
|
|
|
{
|
2019-01-21 09:33:23 +00:00
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
with_raw_value([&] (char const *start, size_t length) {
|
|
|
|
result = (ascii_to(start, out) == length); });
|
|
|
|
|
|
|
|
return result;
|
2015-03-22 02:26:28 +00:00
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2019-01-21 08:34:42 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Return attribute value as 'Genode::String'
|
2019-01-21 08:34:42 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
template <size_t N>
|
|
|
|
void value(String<N> &out) const
|
|
|
|
{
|
|
|
|
with_raw_value([&] (char const *start, size_t length) {
|
|
|
|
out = String<N>(Cstring(start, length)); });
|
|
|
|
}
|
2019-01-21 08:34:42 +00:00
|
|
|
|
2015-09-22 09:52:20 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Return attribute value as 'Genode::String'
|
|
|
|
*
|
|
|
|
* \deprecated use 'value(String<N> &out' instead
|
2015-09-22 09:52:20 +00:00
|
|
|
*/
|
|
|
|
template <size_t N>
|
|
|
|
void value(String<N> *out) const
|
|
|
|
{
|
2019-01-21 09:33:23 +00:00
|
|
|
with_raw_value([&] (char const *start, size_t length) {
|
|
|
|
*out = String<N>(Cstring(start, length)); });
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return attribute value as typed value
|
|
|
|
*
|
|
|
|
* \deprecated use 'value(T &out)' instead
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
bool value(T *out) const
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
with_raw_value([&] (char const *start, size_t length) {
|
|
|
|
result = (ascii_to(start, *out) == length); });
|
|
|
|
|
|
|
|
return result;
|
2015-09-22 09:52:20 +00:00
|
|
|
}
|
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Return next attribute in attribute list
|
|
|
|
*/
|
|
|
|
Xml_attribute next() const { return Xml_attribute(_next()); }
|
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Representation of an XML node
|
|
|
|
*/
|
|
|
|
class Genode::Xml_node
|
|
|
|
{
|
|
|
|
private:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
typedef Xml_attribute::Token Token;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/**
|
|
|
|
* Forward declaration needed for befriending Tag with Xml_attribute
|
|
|
|
*/
|
|
|
|
class Tag;
|
2015-03-04 20:12:14 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
public:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
/*********************
|
|
|
|
** Exception types **
|
|
|
|
*********************/
|
2015-03-04 20:12:14 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
typedef Genode::Exception Exception;
|
|
|
|
typedef Xml_attribute::Nonexistent_attribute Nonexistent_attribute;
|
|
|
|
typedef Xml_attribute::Invalid_syntax Invalid_syntax;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-22 02:26:28 +00:00
|
|
|
class Nonexistent_sub_node : public Exception { };
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Type definition for maintaining backward compatibility
|
|
|
|
*/
|
|
|
|
typedef Xml_attribute Attribute;
|
2015-03-04 20:12:14 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
class Tag
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
enum Type { START, END, EMPTY, INVALID };
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 14:42:15 +00:00
|
|
|
Token _token { };
|
|
|
|
Token _name { };
|
|
|
|
Type _type { INVALID };
|
2015-03-04 20:12:14 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* \param start first token of the tag
|
|
|
|
*
|
|
|
|
* At construction time, the validity of the tag is checked and
|
|
|
|
* the tag type is determined. A valid tag consists of:
|
|
|
|
* # Leading '<' tag delimiter
|
|
|
|
* # '/' for marking an end tag
|
|
|
|
* # Tag name
|
|
|
|
* # Optional attribute sequence (if tag is no end tag)
|
|
|
|
* # '/' for marking an empty-element tag (if tag is no end tag)
|
|
|
|
* # Closing '>' tag delimiter
|
|
|
|
*/
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 14:42:15 +00:00
|
|
|
Tag(Token start) : _token(start)
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
Type supposed_type = START;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
if (_token[0] != '<')
|
|
|
|
return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
if (_token.next()[0] == '/')
|
|
|
|
supposed_type = END;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
if (_token.next().type() != Token::IDENT && _token.next()[0] != '/')
|
|
|
|
return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
_name = _token.next()[0] == '/' ? _token.next().next() : _token.next();
|
|
|
|
if (_name.type() != Token::IDENT)
|
|
|
|
return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* skip attributes to find tag delimiter */
|
|
|
|
Token delimiter = _name.next();
|
|
|
|
if (supposed_type != END)
|
|
|
|
try {
|
2015-03-22 02:26:28 +00:00
|
|
|
for (Xml_attribute a = _name.next(); ; a = a._next())
|
2015-03-04 20:12:14 +00:00
|
|
|
delimiter = a._next();
|
|
|
|
} catch (Nonexistent_attribute) { }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
delimiter = delimiter.eat_whitespace();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/*
|
|
|
|
* Now we expect the '>' delimiter. For empty-element tags,
|
|
|
|
* the delimiter is prefixed with a '/'.
|
|
|
|
*/
|
|
|
|
if (delimiter[0] == '/') {
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* if a '/' was already at the start, the tag is invalid */
|
|
|
|
if (supposed_type == END)
|
|
|
|
return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
supposed_type = EMPTY;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* skip '/' */
|
|
|
|
delimiter = delimiter.next();
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
if (delimiter[0] != '>') return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
_type = supposed_type;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Default constructor produces invalid Tag
|
|
|
|
*/
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 14:42:15 +00:00
|
|
|
Tag() { }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return type of tag
|
|
|
|
*/
|
|
|
|
Type type() const { return _type; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return true if tag is the start of a valid XML node
|
|
|
|
*/
|
2016-05-11 16:21:47 +00:00
|
|
|
bool node() const { return _type == START || _type == EMPTY; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return first token of tag
|
|
|
|
*/
|
|
|
|
Token token() const { return _token; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return name of tag
|
|
|
|
*/
|
|
|
|
Token name() const { return _name; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return token after the closing tag delimiter
|
|
|
|
*/
|
|
|
|
Token next_token() const
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Search for next closing delimiter, skip potential
|
|
|
|
* attributes and '/' delimiter prefix of empty-element
|
|
|
|
* tags.
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2015-03-04 20:12:14 +00:00
|
|
|
Token t = _name;
|
|
|
|
for (; t && t[0] != '>'; t = t.next());
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* if 't' is invalid, 't.next()' is invalid too */
|
|
|
|
return t.next();
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return first attribute of tag
|
|
|
|
*/
|
2015-03-22 02:26:28 +00:00
|
|
|
Xml_attribute attribute() const { return Xml_attribute(_name.next()); }
|
2015-03-04 20:12:14 +00:00
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
class Comment
|
|
|
|
{
|
|
|
|
private:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 14:42:15 +00:00
|
|
|
Token _next { }; /* token following the comment */
|
|
|
|
bool _valid { false }; /* true if comment is well formed */
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
public:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* \param start first token of the comment tag
|
|
|
|
*/
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 14:42:15 +00:00
|
|
|
Comment(Token t)
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
2016-05-28 21:57:18 +00:00
|
|
|
/* check for comment start */
|
|
|
|
if (!t.matches("<!--"))
|
2015-03-04 20:12:14 +00:00
|
|
|
return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-28 21:57:18 +00:00
|
|
|
/* skip four single characters for "<!--" */
|
|
|
|
t = t.next().next().next().next();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-28 21:57:18 +00:00
|
|
|
/* find token after comment delimiter */
|
|
|
|
_next = t.next_after("-->");
|
|
|
|
_valid = _next.valid();
|
2015-03-04 20:12:14 +00:00
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Default constructor produces invalid Comment
|
|
|
|
*/
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 14:42:15 +00:00
|
|
|
Comment() { }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return true if comment is valid
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2015-03-04 20:12:14 +00:00
|
|
|
bool valid() const { return _valid; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return token after the closing comment delimiter
|
|
|
|
*/
|
|
|
|
Token next_token() const { return _next; }
|
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-09-25 13:49:33 +00:00
|
|
|
/**
|
|
|
|
* Helper class to decode XML character entities
|
|
|
|
*/
|
|
|
|
struct Decoded_character
|
|
|
|
{
|
|
|
|
char character = 0;
|
|
|
|
size_t encoded_len = 1;
|
|
|
|
|
|
|
|
struct Translation
|
|
|
|
{
|
|
|
|
char character;
|
|
|
|
char const *seq;
|
|
|
|
size_t seq_len;
|
|
|
|
};
|
|
|
|
|
|
|
|
static Translation translate(char const *src, size_t src_len)
|
|
|
|
{
|
|
|
|
enum { NUM = 6 };
|
|
|
|
static Translation translations[NUM] = {
|
|
|
|
{ '>', ">", 4 },
|
|
|
|
{ '<', "<", 4 },
|
|
|
|
{ '&', "&", 5 },
|
|
|
|
{ '"', """, 6 },
|
|
|
|
{ '\'', "'", 6 },
|
|
|
|
{ 0, "�", 6 }
|
|
|
|
};
|
|
|
|
|
|
|
|
if (src_len == 0)
|
|
|
|
return { 0, nullptr, 0 };
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < NUM; i++) {
|
|
|
|
|
|
|
|
Translation const &translation = translations[i];
|
|
|
|
|
|
|
|
if (src_len < translation.seq_len
|
|
|
|
|| memcmp(src, translation.seq, translation.seq_len))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* translation matches */
|
|
|
|
return translation;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sequence is not known, pass single character as is */
|
|
|
|
return { *src, nullptr, 1 };
|
|
|
|
}
|
|
|
|
|
|
|
|
Decoded_character(char const *src, size_t src_len)
|
|
|
|
{
|
|
|
|
if (*src != '&' || src_len == 0) {
|
|
|
|
character = *src;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Translation const translation = translate(src, src_len);
|
|
|
|
|
|
|
|
character = translation.character;
|
|
|
|
encoded_len = translation.seq_len;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-21 09:33:23 +00:00
|
|
|
int _num_sub_nodes { 0 }; /* number of immediate sub nodes */
|
|
|
|
|
|
|
|
const char * _addr; /* first character of XML data */
|
|
|
|
size_t _max_len; /* length of XML data in characters */
|
|
|
|
Tag _start_tag;
|
|
|
|
Tag _end_tag;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Search matching end tag for given start tag and detemine number of
|
|
|
|
* immediate sub nodes along the way.
|
2015-03-04 20:12:14 +00:00
|
|
|
*
|
|
|
|
* \return end tag or invalid tag
|
|
|
|
*
|
2019-01-21 09:33:23 +00:00
|
|
|
* The method searches for a end tag that matches the same depth level
|
|
|
|
* and the same name as the start tag of the XML node. If the XML
|
|
|
|
* structure is invalid, the search results is an invalid Tag.
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
static Tag _search_end_tag(Tag start_tag, int &sub_nodes_count)
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the start tag is invalid or an empty-element tag,
|
|
|
|
* we use the same tag as end tag.
|
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
if (start_tag.type() != Tag::START)
|
|
|
|
return start_tag;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
int depth = 1;
|
2019-01-21 09:33:23 +00:00
|
|
|
Token curr_token = start_tag.next_token();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
while (curr_token.type() != Token::END) {
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* eat XML comment */
|
|
|
|
Comment curr_comment(curr_token);
|
|
|
|
if (curr_comment.valid()) {
|
|
|
|
curr_token = curr_comment.next_token();
|
|
|
|
continue;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* skip all tokens that are no tags */
|
|
|
|
Tag curr_tag(curr_token);
|
|
|
|
if (curr_tag.type() == Tag::INVALID) {
|
|
|
|
curr_token = curr_token.next();
|
|
|
|
continue;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* count sub nodes at depth 1 */
|
2016-05-11 16:21:47 +00:00
|
|
|
if (depth == 1 && curr_tag.node())
|
2019-01-21 09:33:23 +00:00
|
|
|
sub_nodes_count++;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* keep track of the current depth */
|
|
|
|
depth += (curr_tag.type() == Tag::START);
|
|
|
|
depth -= (curr_tag.type() == Tag::END);
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* within sub nodes, continue after current token */
|
|
|
|
if (depth > 0) {
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* continue search with token after current tag */
|
|
|
|
curr_token = curr_tag.next_token();
|
|
|
|
continue;
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* reaching the same depth as the start tag */
|
2019-01-21 09:33:23 +00:00
|
|
|
const char *start_name = start_tag.name().start();
|
|
|
|
size_t start_len = start_tag.name().len();
|
|
|
|
const char *curr_name = curr_tag.name().start();
|
|
|
|
size_t curr_len = curr_tag.name().len();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* on mismatch of start tag and end tag, return invalid tag */
|
|
|
|
if (start_len != curr_len
|
|
|
|
|| strcmp(start_name, curr_name, curr_len))
|
|
|
|
return Tag();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* end tag corresponds to start tag */
|
|
|
|
return curr_tag;
|
|
|
|
}
|
|
|
|
return Tag();
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Find next non-whitespace and non-comment token
|
|
|
|
*/
|
2015-04-19 21:09:28 +00:00
|
|
|
static Token skip_non_tag_characters(Token t)
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
t = t.eat_whitespace();
|
|
|
|
|
|
|
|
/* eat comment */
|
|
|
|
Comment comment(t);
|
|
|
|
if (comment.valid()) {
|
|
|
|
t = comment.next_token();
|
|
|
|
continue;
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
|
2015-04-19 21:09:28 +00:00
|
|
|
/* skip token if it is valid but does not start a tag */
|
|
|
|
Tag curr_tag(t);
|
|
|
|
if (curr_tag.type() == Tag::INVALID && curr_tag.token()) {
|
|
|
|
t = t.next();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
break;
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
return t;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Create sub node from XML node
|
|
|
|
*
|
|
|
|
* \throw Nonexistent_sub_node
|
|
|
|
* \throw Invalid_syntax
|
|
|
|
*/
|
|
|
|
Xml_node _sub_node(const char *at) const
|
|
|
|
{
|
2019-01-21 09:33:23 +00:00
|
|
|
if (at < _addr || (size_t)(at - _addr) >= _max_len)
|
2015-03-04 20:12:14 +00:00
|
|
|
throw Nonexistent_sub_node();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2019-01-21 09:33:23 +00:00
|
|
|
return Xml_node(at, _max_len - (at - _addr));
|
2015-03-04 20:12:14 +00:00
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2019-01-21 09:33:23 +00:00
|
|
|
/**
|
|
|
|
* Return pointer to start of content
|
|
|
|
*/
|
|
|
|
char const *_content_base() const { return _start_tag.next_token().start(); }
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
public:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
2016-05-09 15:08:33 +00:00
|
|
|
* The constructor validates if the start tag has a matching end tag of
|
|
|
|
* the same depth and counts the number of immediate sub nodes.
|
|
|
|
*
|
|
|
|
* \throw Invalid_syntax
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
Xml_node(const char *addr, size_t max_len = ~0UL)
|
|
|
|
:
|
2015-03-04 20:12:14 +00:00
|
|
|
_addr(addr),
|
|
|
|
_max_len(max_len),
|
2015-04-19 21:09:28 +00:00
|
|
|
_start_tag(skip_non_tag_characters(Token(addr, max_len))),
|
2019-01-21 09:33:23 +00:00
|
|
|
_end_tag(_search_end_tag(_start_tag, _num_sub_nodes))
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
/* check validity of XML node */
|
|
|
|
if (_start_tag.type() == Tag::EMPTY) return;
|
|
|
|
if (_start_tag.type() == Tag::START && _end_tag.type() == Tag::END) return;
|
|
|
|
|
|
|
|
throw Invalid_syntax();
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Request type name of XML node as null-terminated string
|
|
|
|
*/
|
|
|
|
void type_name(char *dst, size_t max_len) const {
|
|
|
|
_start_tag.name().string(dst, max_len); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2019-01-21 09:33:23 +00:00
|
|
|
/**
|
|
|
|
* Return size of node including start and end tags in bytes
|
|
|
|
*/
|
|
|
|
size_t size() const { return _end_tag.next_token().start() - _addr; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return pointer to start of node
|
|
|
|
*
|
|
|
|
* \deprecated use 'with_raw_node' instead
|
|
|
|
*/
|
|
|
|
char const *addr() const { return _addr; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return size of node content
|
|
|
|
*/
|
|
|
|
size_t content_size() const
|
|
|
|
{
|
|
|
|
if (_start_tag.type() == Tag::EMPTY)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return _end_tag.token().start() - _content_base();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request type name of XML node as null-terminated string
|
|
|
|
*/
|
2015-11-23 12:34:17 +00:00
|
|
|
typedef String<64> Type;
|
|
|
|
Type type() const
|
|
|
|
{
|
|
|
|
Token name = _start_tag.name();
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
return Type(Cstring(name.start(), name.len()));
|
2015-11-23 12:34:17 +00:00
|
|
|
}
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return true if tag is of specified type
|
|
|
|
*/
|
|
|
|
bool has_type(const char *type) const {
|
|
|
|
return (!strcmp(type, _start_tag.name().start(),
|
|
|
|
_start_tag.name().len())
|
|
|
|
&& strlen(type) == _start_tag.name().len()); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Call functor 'fn' with the node data '(char const *, size_t)'
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
template <typename FN>
|
|
|
|
void with_raw_node(FN const &fn) const
|
|
|
|
{
|
|
|
|
fn(_addr, _end_tag.next_token().start() - _addr);
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Call functor 'fn' with content '(char const *, size_t) as argument'
|
2015-03-04 20:12:14 +00:00
|
|
|
*
|
2019-01-21 09:33:23 +00:00
|
|
|
* Note that the content is not null-terminated. It points directly
|
|
|
|
* into a sub range of the unmodified 'Xml_node' data.
|
2019-01-21 08:34:42 +00:00
|
|
|
*
|
2019-01-21 09:33:23 +00:00
|
|
|
* If the node has no content, the functor 'fn' is not called.
|
2019-01-21 08:34:42 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
template <typename FN>
|
|
|
|
void with_raw_content(FN const &fn) const
|
|
|
|
{
|
|
|
|
if (_start_tag.type() == Tag::EMPTY)
|
|
|
|
return;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2019-01-21 09:33:23 +00:00
|
|
|
fn(_content_base(), content_size());
|
|
|
|
}
|
2014-01-21 21:32:18 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Return pointer to start of content
|
2015-03-04 20:12:14 +00:00
|
|
|
*
|
2019-01-21 09:33:23 +00:00
|
|
|
* XXX This method is deprecated. Use 'with_raw_content()' instead.
|
2015-05-13 20:11:46 +00:00
|
|
|
*
|
|
|
|
* \noapi
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
|
|
|
char *content_addr() const { return _start_tag.next_token().start(); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return pointer to start of content
|
2019-01-21 09:33:23 +00:00
|
|
|
*
|
|
|
|
* XXX This method is deprecated. Use 'with_raw_content()' instead.
|
|
|
|
*
|
|
|
|
* \noapi
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
|
|
|
char const *content_base() const { return content_addr(); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Return content as out value
|
|
|
|
*
|
|
|
|
* \deprecated use with_raw_content instead
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
2019-01-21 09:33:23 +00:00
|
|
|
template <typename T> bool value(T *out) const { return value(*out); }
|
2015-09-25 13:49:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Export decoded node content from XML node
|
|
|
|
*
|
|
|
|
* \param dst destination buffer
|
|
|
|
* \param dst_len size of destination buffer in bytes
|
|
|
|
* \return number of bytes written to the destination buffer
|
|
|
|
*
|
|
|
|
* This function transforms XML character entities into their
|
|
|
|
* respective characters.
|
|
|
|
*/
|
|
|
|
size_t decoded_content(char *dst, size_t dst_len) const
|
|
|
|
{
|
|
|
|
size_t result_len = 0;
|
2019-01-21 09:33:23 +00:00
|
|
|
char const *src = _content_base();
|
|
|
|
size_t src_len = content_size();
|
2015-09-25 13:49:33 +00:00
|
|
|
|
2018-01-16 13:30:30 +00:00
|
|
|
for (; dst_len && src_len; dst_len--, result_len++) {
|
2015-09-25 13:49:33 +00:00
|
|
|
|
|
|
|
Decoded_character const decoded_character(src, src_len);
|
|
|
|
|
|
|
|
*dst++ = decoded_character.character;
|
|
|
|
|
|
|
|
src += decoded_character.encoded_len;
|
|
|
|
src_len -= decoded_character.encoded_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read decoded node content as Genode::String
|
|
|
|
*/
|
|
|
|
template <typename STRING>
|
|
|
|
STRING decoded_content() const
|
|
|
|
{
|
|
|
|
char buf[STRING::capacity() + 1];
|
2018-01-16 13:43:24 +00:00
|
|
|
size_t const len = decoded_content(buf, sizeof(buf) - 1);
|
|
|
|
buf[min(len, sizeof(buf) - 1)] = 0;
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
return STRING(Cstring(buf));
|
2015-09-25 13:49:33 +00:00
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return the number of the XML node's immediate sub nodes
|
|
|
|
*/
|
|
|
|
size_t num_sub_nodes() const { return _num_sub_nodes; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return XML node following the current one
|
|
|
|
*
|
|
|
|
* \throw Nonexistent_sub_node sub sequent node does not exist
|
|
|
|
*/
|
|
|
|
Xml_node next() const
|
|
|
|
{
|
|
|
|
Token after_node = _end_tag.next_token();
|
2015-04-19 21:09:28 +00:00
|
|
|
after_node = skip_non_tag_characters(after_node);
|
2015-03-04 20:12:14 +00:00
|
|
|
try { return _sub_node(after_node.start()); }
|
|
|
|
catch (Invalid_syntax) { throw Nonexistent_sub_node(); }
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return next XML node of specified type
|
|
|
|
*
|
2019-01-21 09:33:23 +00:00
|
|
|
* \param type type of XML node, or nullptr for matching any type
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
|
|
|
Xml_node next(const char *type) const
|
|
|
|
{
|
|
|
|
Xml_node node = next();
|
|
|
|
for (; type && !node.has_type(type); node = node.next());
|
|
|
|
return node;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return true if node is the last of a node sequence
|
|
|
|
*/
|
2016-05-11 16:21:47 +00:00
|
|
|
bool last(const char *type = 0) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
try { next(type); return false; }
|
|
|
|
catch (Nonexistent_sub_node) { return true; }
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return sub node with specified index
|
|
|
|
*
|
|
|
|
* \param idx index of sub node,
|
|
|
|
* default is the first node
|
|
|
|
* \throw Nonexistent_sub_node no such sub node exists
|
|
|
|
*/
|
|
|
|
Xml_node sub_node(unsigned idx = 0U) const
|
|
|
|
{
|
|
|
|
if (_num_sub_nodes > 0) {
|
|
|
|
|
|
|
|
/* look up node at specified index */
|
|
|
|
try {
|
2019-01-21 09:33:23 +00:00
|
|
|
Xml_node curr_node = _sub_node(_content_base());
|
2015-03-04 20:12:14 +00:00
|
|
|
for (; idx > 0; idx--)
|
|
|
|
curr_node = curr_node.next();
|
|
|
|
return curr_node;
|
|
|
|
} catch (Invalid_syntax) { }
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
throw Nonexistent_sub_node();
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return first sub node that matches the specified type
|
|
|
|
*
|
|
|
|
* \throw Nonexistent_sub_node no such sub_node exists
|
|
|
|
*/
|
|
|
|
Xml_node sub_node(const char *type) const
|
|
|
|
{
|
|
|
|
if (_num_sub_nodes > 0) {
|
|
|
|
|
|
|
|
/* search for sub node of specified type */
|
|
|
|
try {
|
2019-01-21 09:33:23 +00:00
|
|
|
Xml_node curr_node = _sub_node(_content_base());
|
2015-03-04 20:12:14 +00:00
|
|
|
for ( ; true; curr_node = curr_node.next())
|
|
|
|
if (curr_node.has_type(type))
|
|
|
|
return curr_node;
|
|
|
|
} catch (...) { }
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
throw Nonexistent_sub_node();
|
|
|
|
}
|
2014-09-30 15:55:01 +00:00
|
|
|
|
2019-01-21 09:33:23 +00:00
|
|
|
/**
|
|
|
|
* Apply functor 'fn' to first sub node of specified type
|
|
|
|
*
|
|
|
|
* The functor is called with the sub node as argument.
|
|
|
|
* If no matching sub node exists, the functor is not called.
|
|
|
|
*/
|
|
|
|
template <typename FN>
|
|
|
|
void with_sub_node(char const *type, FN const &fn) const
|
|
|
|
{
|
|
|
|
if (has_sub_node(type))
|
|
|
|
fn(sub_node(type));
|
|
|
|
}
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Execute functor 'fn' for each sub node of specified type
|
|
|
|
*/
|
|
|
|
template <typename FN>
|
2015-11-30 13:59:37 +00:00
|
|
|
void for_each_sub_node(char const *type, FN const &fn) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
if (_num_sub_nodes == 0)
|
|
|
|
return;
|
2014-09-30 15:55:01 +00:00
|
|
|
|
2019-03-14 15:31:11 +00:00
|
|
|
try {
|
|
|
|
Xml_node node = sub_node();
|
|
|
|
for (int i = 0; ; node = node.next()) {
|
2014-09-30 15:55:01 +00:00
|
|
|
|
2019-03-14 15:31:11 +00:00
|
|
|
if (!type || node.has_type(type))
|
|
|
|
fn(node);
|
2014-09-30 15:55:01 +00:00
|
|
|
|
2019-03-14 15:31:11 +00:00
|
|
|
if (++i == _num_sub_nodes)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (Nonexistent_sub_node) { }
|
2015-03-04 20:12:14 +00:00
|
|
|
}
|
2014-09-30 15:55:01 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Execute functor 'fn' for each sub node
|
|
|
|
*/
|
|
|
|
template <typename FN>
|
2015-11-30 13:59:37 +00:00
|
|
|
void for_each_sub_node(FN const &fn) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
for_each_sub_node(nullptr, fn);
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return Nth attribute of XML node
|
|
|
|
*
|
|
|
|
* \param idx attribute index,
|
|
|
|
* first attribute has index 0
|
|
|
|
* \throw Nonexistent_attribute no such attribute exists
|
|
|
|
* \return XML attribute
|
|
|
|
*/
|
2015-03-22 02:26:28 +00:00
|
|
|
Xml_attribute attribute(unsigned idx) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
/* get first attribute of the node */
|
2015-03-22 02:26:28 +00:00
|
|
|
Xml_attribute a = _start_tag.attribute();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* skip attributes until we reach the target index */
|
|
|
|
for (; idx > 0; idx--)
|
|
|
|
a = a._next();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
return a;
|
|
|
|
}
|
2013-08-08 10:47:24 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return attribute of specified type
|
|
|
|
*
|
|
|
|
* \param type name of attribute type
|
|
|
|
* \throw Nonexistent_attribute no such attribute exists
|
|
|
|
* \return XML attribute
|
|
|
|
*/
|
2015-03-22 02:26:28 +00:00
|
|
|
Xml_attribute attribute(const char *type) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
/* iterate, beginning with the first attribute of the node */
|
2015-03-22 02:26:28 +00:00
|
|
|
for (Xml_attribute a = _start_tag.attribute(); ; a = a.next())
|
2015-03-04 20:12:14 +00:00
|
|
|
if (a.has_type(type))
|
|
|
|
return a;
|
|
|
|
}
|
2013-08-08 10:47:24 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
2019-01-21 09:33:23 +00:00
|
|
|
* Read attribute value from XML node
|
2015-03-04 20:12:14 +00:00
|
|
|
*
|
|
|
|
* \param type attribute name
|
|
|
|
* \param default_value value returned if no attribute with the
|
|
|
|
* name 'type' is present.
|
|
|
|
* \return attribute value or specified default value
|
|
|
|
*
|
2019-01-21 09:33:23 +00:00
|
|
|
* The type of the return value corresponds to the type of the
|
|
|
|
* default value.
|
2015-03-04 20:12:14 +00:00
|
|
|
*/
|
|
|
|
template <typename T>
|
2019-01-21 09:33:23 +00:00
|
|
|
inline T attribute_value(char const *type, T const default_value) const
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
T result = default_value;
|
2019-01-21 09:33:23 +00:00
|
|
|
try { attribute(type).value(result); } catch (...) { }
|
2015-03-04 20:12:14 +00:00
|
|
|
return result;
|
|
|
|
}
|
2013-11-19 13:18:08 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Return true if attribute of specified type exists
|
|
|
|
*/
|
|
|
|
inline bool has_attribute(char const *type) const
|
|
|
|
{
|
|
|
|
try { attribute(type); return true; } catch (...) { }
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if sub node of specified type exists
|
|
|
|
*/
|
|
|
|
inline bool has_sub_node(char const *type) const
|
|
|
|
{
|
|
|
|
try { sub_node(type); return true; } catch (...) { }
|
|
|
|
return false;
|
|
|
|
}
|
2016-09-13 09:26:18 +00:00
|
|
|
|
|
|
|
void print(Output &output) const {
|
2019-01-21 09:33:23 +00:00
|
|
|
output.out_string(_addr, size()); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if this node differs from 'another'
|
|
|
|
*/
|
|
|
|
bool differs_from(Xml_node const &another) const
|
|
|
|
{
|
|
|
|
return size() != another.size() ||
|
|
|
|
memcmp(_addr, another._addr, size()) != 0;
|
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
#endif /* _INCLUDE__UTIL__XML_NODE_H_ */
|