blob: 26f6c74bcfa978d432445a63aa9eb733bb365efd [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2020 Red Hat, Inc.
//
// Author: Dodji Seketeli
/// @file
///
/// This file contains the definitions for the ini file reader used in
/// the libabigail library.
#include <cassert>
#include <cstdlib>
#include <utility>
#include <memory>
#include <fstream>
#include <sstream>
#include "abg-fwd.h"
#include "abg-internal.h"
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS
#include "abg-ini.h"
ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>
namespace abigail
{
namespace ini
{
using std::istream;
using std::pair;
static bool
char_is_white_space(int b);
static bool
char_is_comment_start(int b);
/// Test if a given character is a delimiter.
///
///
///@param b the value of the character to test for.
///
///@param include_white_space if true, consider white spaces as a
///delimiter.
///
///@param include_square_bracket if true, consider square brackets as
/// delimiters
///
/// @param include_equal if true, consider the equal character ('=')
/// as a delimiter.
///
/// @return true iff @p b is a delimiter.
static bool
char_is_delimiter(int b, bool include_white_space = true,
bool include_square_bracket = true,
bool include_equal = true)
{
return ((include_square_bracket && (b == '['))
|| (include_square_bracket && (b == ']'))
|| b == '{'
|| b == '}'
|| (include_equal && (b == '='))
|| b == ','
|| (include_white_space && char_is_white_space(b))
|| char_is_comment_start(b));
}
/// Return true iff a given character can be part of a property
/// value.
///
/// Note that white spaces, square brackets and the equal character can be
/// part of a property value. The reason why we accept the equal
/// character is because it can appear in an URL.
///
/// @param b the character to test against.
///
/// @return true iff @p b is a character that can be part of a
/// property value.
static bool
char_is_property_value_char(int b)
{
if (char_is_delimiter(b, /*include_white_space=*/false,
/*include_square_bracket=*/false,
/*include_equal=*/false)
|| b == '\n')
return false;
return true;
}
/// Test if a given character is meant to be part of a section name.
///
/// @param b the character to test against.
///
/// @return true iff @p b is a character that is meant to be part of
/// a section name.
static bool
char_is_section_name_char(int b)
{
if (b == '[' || b == ']' || b == '\n' || char_is_comment_start(b))
return false;
return true;
}
/// Test if a given character is meant to be part of a property name.
///
/// @param b the character to test against.
///
/// @return true iff @p b is a character that is meant to be part of
/// a section name.
static bool
char_is_property_name_char(int b)
{
if (char_is_delimiter(b))
return false;
return true;
}
/// Test if a given character is meant to be part of a function name.
///
/// @param b the character to test against.
///
/// @return true iff @p b is a character that is meant to be part of a
/// function name.
static bool
char_is_function_name_char(int b)
{
if (char_is_delimiter(b) || b == '(' || b == ')')
return false;
return true;
}
/// Test if a given character is meant to be part of a function
/// argument.
///
/// @param b the character to test against.
///
/// @return true iff @p b is a character that is meant to be part of a
/// function argument.
static bool
char_is_function_argument_char(int b)
{
if (char_is_delimiter(b) || b == '(' || b == ')')
return false;
return true;
}
/// Test if a given character is meant to be the start of a comment.
///
/// @param b the character to test against.
///
/// @return true iff @p b is the start of a comment.
static bool
char_is_comment_start(int b)
{return b == ';' || b == '#';}
/// Test if a character is meant to be a white space.
///
/// @param b the character to test against.
///
/// @return true iff @p b is a white space.
static bool
char_is_white_space(int b)
{return b == ' ' || b == '\t' || b == '\n';}
/// Remove the spaces at the begining and at the end of a given string.
///
/// @param str the string to remove leading and trailing white spaces from.
///
/// @return the string resulting from the removal of white space from @p str.
static string
trim_white_space(const string& str)
{
if (str.empty())
return str;
unsigned s = 0, e = str.size() -1;
for (; s <= e; ++s)
if (!char_is_white_space(str[s]))
break;
for (; e > s; --e)
if (!char_is_white_space(str[e]))
break;
return str.substr(s, e - s + 1);
}
// <property stuff>
/// Private data of @ref property type.
struct property::priv
{
string name_;
priv()
{}
priv(const string& name)
: name_(name)
{}
}; // end struct property::priv
/// Constructor of @ref property.
property::property()
: priv_(new priv)
{}
/// Constructor of @ref property
///
/// @param name the name of the property.
property::property(const string& name)
: priv_(new priv(name))
{}
/// Getter of the name of the property.
///
/// @return the name of the property.
const string&
property::get_name()const
{return priv_->name_;}
/// Setter of the name of the property.
///
/// @param name the new name of the property.
void
property::set_name(const string& name)
{priv_->name_ = name;}
/// Destructor of the property.
property::~property()
{}
// </property stuff>
// <property_value stuff>
/// Private data for the @ref property_value type.
struct property_value::priv
{
enum property_value::value_kind kind_;
priv(property_value::value_kind kind = ABSTRACT_PROPERTY_VALUE)
: kind_(kind)
{}
}; // property_value::priv
/// Default constructor for the @ref property_value type.
///
/// @param kind the of @ref property_value that is being constructed.
property_value::property_value()
: priv_(new priv(ABSTRACT_PROPERTY_VALUE))
{}
/// Constructor for the @ref property_value type.
///
/// @param kind the of @ref property_value that is being constructed.
property_value::property_value(value_kind kind)
: priv_(new priv(kind))
{}
/// Getter for the kind of the @ref property_value type.
///
/// @return the kind of @ref property_value we are looking at.
property_value::value_kind
property_value::get_kind() const
{return priv_->kind_;}
/// Converts the current property value to a string.
///
/// @return the string representation of the property value.
property_value::operator const string& () const
{return as_string();}
/// Destructor for the @ref proprerty_value type.
property_value::~property_value()
{}
// </property_value stuff>
// <string_property stuff>
/// The private data for the @ref string_property_value type.
struct string_property_value::priv
{
string content_;
priv()
{}
priv(const string& c)
: content_(c)
{}
}; // end struct string_property::priv
/// Constructor of the @ref string_property_value type.
string_property_value::string_property_value()
: property_value(STRING_PROPERTY_VALUE),
priv_(new priv())
{}
/// Constructor of the @ref string_property_value.
///
/// @param content the string content of the property value.
string_property_value::string_property_value(const string& content)
: property_value(STRING_PROPERTY_VALUE),
priv_(new priv(content))
{}
/// Setter of the content of the string property value.
///
/// @param c the new content.
void
string_property_value::set_content(const string& c)
{priv_->content_ = c;}
/// Convert the string property value into a string.
///
/// @return the string contained in the string property value.
const string&
string_property_value::as_string() const
{return priv_->content_;}
/// Conversion operator to a string, for the @ref
/// string_property_value type.
///
/// @return the string representing this string_property_value.
string_property_value::operator string() const
{return as_string();}
/// Test if a given property value is a string property value.
///
/// @return a pointer to the @ref string_property_value sub-object of
/// the @ref property_value instance, if it's an instance of @ref
/// string_property_value too.
string_property_value*
is_string_property_value(const property_value* v)
{return dynamic_cast<string_property_value*>(const_cast<property_value*>(v));}
/// Test if a given property value is a string property value.
///
/// @return a pointer to the @ref string_property_value sub-object of
/// the @ref property_value instance, if it's an instance of @ref
/// string_property_value too.
string_property_value_sptr
is_string_property_value(const property_value_sptr v)
{return dynamic_pointer_cast<string_property_value>(v);}
/// Destructor for the @ref string_property_value
string_property_value::~string_property_value()
{}
// </string_property_value stuff>
// <list_property_value stuff>
struct list_property_value::priv
{
vector<string> values_;
string representation_;
priv()
{}
priv(const vector<string>& vals)
: values_(vals)
{}
}; // end struct list_property_value::priv
/// Default constructor of the @ref list_property_value type.
list_property_value::list_property_value()
: property_value(property_value::LIST_PROPERTY_VALUE),
priv_(new priv)
{}
/// Copy constructor of the @ref list_property_value type.
///
/// @param values the instance of @ref list_property_value to copy from.
list_property_value::list_property_value(const vector<string>& values)
: property_value(property_value::LIST_PROPERTY_VALUE),
priv_(new priv(values))
{
}
/// Getter of the content of the @ref list_property_value.
///
/// The content of the @ref list_property_value is a vector of
/// strings.
///
/// @return the vector of strings contained in the @ref
/// list_property_value.
const vector<string>&
list_property_value::get_content() const
{return priv_->values_;}
/// Setter of the content of the @ref list_property_value.
///
/// @param values the new content, which is a vector of strings.
void
list_property_value::set_content(const vector<string>& values)
{
priv_->values_ = values;
priv_->representation_.clear();
}
/// Return a string representation of the @list_property_value.
///
/// @return the string representation.
const string&
list_property_value::as_string() const
{
if (priv_->representation_.empty())
{
for (vector<string>::const_iterator i = priv_->values_.begin();
i != priv_->values_.end();
++i)
{
if (i != priv_->values_.begin())
priv_->representation_ += ",";
priv_->representation_ += *i;
}
}
return priv_->representation_;
}
/// Test if an instance of @property_value is a @ref list_property_value.
///
/// @param v the property_value to consider.
///
/// @return the @ref property_value converted into a @ref
/// list_property_value if the @p v is a @ref list_property_value, nil
/// otherwise.
list_property_value*
is_list_property_value(const property_value* v)
{return dynamic_cast<list_property_value*>(const_cast<property_value*>(v));}
/// Test if an instance of @property_value is a @ref list_property_value.
///
/// @param v the property_value to consider.
///
/// @return the @ref property_value converted into a @ref
/// list_property_value if the @p v is a @ref list_property_value, nil
/// otherwise.
list_property_value_sptr
is_list_property_value(const property_value_sptr&v)
{return dynamic_pointer_cast<list_property_value>(v);}
// </list_property_value stuff>
// <tuple_property_value>
/// The private data of the @ref tuple_property_value type.
struct tuple_property_value::priv
{
vector<property_value_sptr> value_items_;
string string_rep_;
priv()
{}
priv(const vector<property_value_sptr>& value_items)
: value_items_(value_items)
{}
}; // end struct tuple_property_value::priv
/// Constructor for the @ref tuple_property_value type.
///
/// @param v the tuple content of the value.
tuple_property_value::tuple_property_value(const vector<property_value_sptr>& v)
: property_value(TUPLE_PROPERTY_VALUE),
priv_(new priv(v))
{}
/// Getter for the content of the @ref tuple_property_value instance.
///
/// @return the content of the @ref tuple_property_value instance.
const vector<property_value_sptr>&
tuple_property_value::get_value_items() const
{return priv_->value_items_;}
/// Getter for the content of the @ref tuple_property_value instance.
///
/// @return the content of the @ref tuple_property_value instance.
vector<property_value_sptr>&
tuple_property_value::get_value_items()
{return priv_->value_items_;}
/// Destructor of the @ref tuple_property_value type.
tuple_property_value::~tuple_property_value()
{}
/// Convert to the instance of @ref tuple_property_value to a string.
///
/// @return the string representation of the @ref tuple_property_value.
const string&
tuple_property_value::as_string() const
{
if (priv_->string_rep_.empty())
{
priv_->string_rep_ += '{';
for (vector<property_value_sptr>::const_iterator i =
get_value_items().begin();
i != get_value_items().end();
++i)
{
if (i != get_value_items().begin())
priv_->string_rep_ += ",";
priv_->string_rep_ += (*i)->as_string();
}
priv_->string_rep_ += '}';
}
return priv_->string_rep_;
}
/// Test if a given instance of @ref property_value is an instance of
/// @ref tuple_property_value too.
///
/// @return the @ref tuple_property_value sub-object of the @ref
/// property_value instance, if it's an instance of @ref
/// tuple_property_value too.
tuple_property_value*
is_tuple_property_value(const property_value* v)
{return dynamic_cast<tuple_property_value*>(const_cast<property_value*>(v));}
/// Test if a given instance of @ref property_value is an instance of
/// @ref tuple_property_value too.
///
/// @return the @ref tuple_property_value sub-object of the @ref
/// property_value instance, if it's an instance of @ref
/// tuple_property_value too.
tuple_property_value_sptr
is_tuple_property_value(const property_value_sptr v)
{return dynamic_pointer_cast<tuple_property_value>(v);}
// </tuple_property_value>
// <simple_property stuff>
/// Private data of the @ref simple_property type.
struct simple_property::priv
{
string_property_value_sptr value_;
priv()
{}
priv(const string_property_value_sptr value)
: value_(value)
{}
}; // end struct simple_property::priv
/// Default constructor of the @ref simple_property type.
simple_property::simple_property()
: property(),
priv_(new priv)
{}
/// Constructor for the @ref simple_property type.
///
/// @param name the name of the property.
///
/// @param value the value of the property.
simple_property::simple_property(const string& name,
const string_property_value_sptr& value)
: property(name),
priv_(new priv(value))
{}
/// Constructor for the @ref simple_property type.
///
/// This one constructs a property with an empty value.
///
/// @param name the name of the property.
simple_property::simple_property(const string& name)
: property(name),
priv_(new priv)
{}
/// Getter for the string value of the property.
///
/// @return the string value of the property.
const string_property_value_sptr&
simple_property::get_value() const
{return priv_->value_;}
/// Setter for the string value of the property.
///
/// @param value the new string value of the property.
void
simple_property::set_value(const string_property_value_sptr& value)
{priv_->value_ = value;}
/// Test if the property has an empty value.
///
/// An empty value is either no value at all or an empty string value.
///
/// @return true iff the property has an empty value.
bool
simple_property::has_empty_value() const
{
if (!priv_->value_)
return true;
return priv_->value_->as_string().empty();
}
/// Destructor of the @ref simple_property type.
simple_property::~simple_property()
{}
/// Tests if a @ref property is a simple property.
///
/// @return a pointer to the @ref simple_property sub-object of the
/// @ref property instance, iff it's an @ref simple_property
/// instance.
simple_property*
is_simple_property(const property* p)
{return dynamic_cast<simple_property*>(const_cast<property*>(p));}
/// Tests if a @ref property is a simple property.
///
/// @return a smart pointer to the @ref simple_property sub-object of
/// the @ref property instance, iff it's an @ref simple_property
/// instance.
simple_property_sptr
is_simple_property(const property_sptr p)
{return dynamic_pointer_cast<simple_property>(p);}
// </simple_property stuff>
// <list_property stuff>
struct list_property::priv
{
list_property_value_sptr value_;
priv()
{}
priv(const list_property_value_sptr value)
: value_(value)
{}
}; //end struct list_property
/// Default constructor for @ref list_property.
list_property::list_property()
: priv_(new priv)
{}
/// Constructor for @ref list_property.
///
/// @param name the name of the property.
///
/// @param value the value of the property.
list_property::list_property(const string& name,
const list_property_value_sptr& value)
: property(name),
priv_(new priv(value))
{}
/// Getter for the value of the @ref list_property_value
const list_property_value_sptr&
list_property::get_value() const
{return priv_->value_;}
/// Setter for the value of the @ref list_property.
///
/// @param value the new value.
void
list_property::set_value(const list_property_value_sptr& value)
{priv_->value_ = value;}
/// Destructor of the @ref list_property type.
list_property::~list_property()
{}
/// Test if an instance of a @ref property is actually an instance of
/// @ref list_property.
///
/// @param p the @ref property to test.
///
/// @return the @p p converted into a @ref list_property if it's of
/// type @ref list_property, or nil otherwise.
list_property*
is_list_property(const property* p)
{return dynamic_cast<list_property*>(const_cast<property*>(p));}
/// Test if an instance of a @ref property is actually an instance of
/// @ref list_property.
///
/// @param p the @ref property to test.
///
/// @return the @p p converted into a @ref list_property if it's of
/// type @ref list_property, or nil otherwise.
list_property_sptr
is_list_property(const property_sptr p)
{return dynamic_pointer_cast<list_property>(p);}
// </list_property stuff>
// <tuple_property stuff>
struct tuple_property::priv
{
tuple_property_value_sptr value_;
priv()
{}
priv(const tuple_property_value_sptr value)
: value_(value)
{}
}; // end struct tuple_property::priv
/// Default constructor of the @ref tuple_property type.
tuple_property::tuple_property()
: property(),
priv_(new priv)
{}
/// Constructor of the @ref tuple_property type.
///
/// @param name the name of the property.
///
/// @param values the tuple value of the property.
tuple_property::tuple_property(const string& name,
const tuple_property_value_sptr value)
: property(name),
priv_(new priv(value))
{}
/// Setter for the tuple value of the property.
///
/// @param values the new tuple value of the property.
void
tuple_property::set_value(const tuple_property_value_sptr value)
{priv_->value_ = value;}
/// Getter for the tuple value of the property.
///
/// @return the tuple value of the property.
const tuple_property_value_sptr&
tuple_property::get_value() const
{return priv_->value_;}
/// Destructor for the @ref tuple_property type.
tuple_property::~tuple_property()
{}
/// Test if an instance of @ref property is an instance of @ref
/// tuple_property.
///
/// @param p the instance of @ref property to test for.
///
/// @return return a pointer to the sub-object of @ref tuple_property
/// iff @p p is an instance of @ref tuple_property.
tuple_property*
is_tuple_property(const property* p)
{return dynamic_cast<tuple_property*>(const_cast<property*>(p));}
/// Test if an instance of @ref property is an instance of @ref
/// tuple_property.
///
/// @param p the instance of @ref property to test for.
///
/// @return return a smart pointer to the sub-object of @ref
/// tuple_property iff @p p is an instance of @ref tuple_property.
tuple_property_sptr
is_tuple_property(const property_sptr p)
{return dynamic_pointer_cast<tuple_property>(p);}
// </tuple_property stuff>
class config::section::priv
{
string name_;
properties_type properties_;
// Forbid this;
priv();
public:
priv(const string& name)
: name_(name)
{}
friend class config::section;
};//end struct config::section::priv
// <config::section stuff>
/// Constructor for config::section.
///
/// @param name the name of the ini section.
config::section::section(const string& name)
: priv_(new priv(name))
{}
/// Constructor for the config::section.
///
/// @param name the name of the ini section.
///
/// @param properties the properties of the section.
config::section::section(const string& name,
const properties_type& properties)
: priv_(new priv(name))
{set_properties(properties);}
/// Get the name of the section.
///
/// @return the name of the section.
const string&
config::section::get_name() const
{return priv_->name_;}
/// Get the properties of the section.
///
/// @return a vector of the properties of the section.
const config::properties_type&
config::section::get_properties() const
{return priv_->properties_;}
/// Set the properties of the section.
///
/// @param properties the new properties to set.
void
config::section::set_properties(const properties_type& properties)
{priv_->properties_ = properties;}
/// Add one property to this section.
///
/// @param prop the property to add to the section.
void
config::section::add_property(const property_sptr prop)
{priv_->properties_.push_back(prop);}
/// Find a property that has a given name.
///
/// Note that this only returns the first property with that name.
///
/// @param prop_name the name of the property to find.
///
/// @return the found property, or nil if no property with the name @p
/// prop_name was found.
property_sptr
config::section::find_property(const string& prop_name) const
{
for (properties_type::const_iterator i = get_properties().begin();
i != get_properties().end();
++i)
if ((*i)->get_name() == prop_name)
return *i;
return property_sptr();
}
/// Destructor of config::section.
config::section::~section()
{}
// /<config::section stuff>
// <read_context stuff>
/// The context of the ini file parsing.
///
/// This is a private type that is used only in the internals of the
/// ini file parsing.
class read_context
{
/// The input stream we are parsing from.
istream& in_;
/// The current line being parsed.
unsigned cur_line_;
/// The current column on the current line.
unsigned cur_column_;
vector<char> buf_;
// Forbid this;
read_context();
public:
/// The constructor of @ref read_context.
///
/// @param in the input stream to parse from.
read_context(istream& in)
: in_(in),
cur_line_(0),
cur_column_(0)
{}
/// @return the character that is going to be read by the next
/// invocation of read_next_char().
///
/// Note that this function doesn't alter the input stream.
///
/// Also note that this function handles escaping using the '\'
/// (backslash) character.
///
/// @param escaped This is an output parameter. It's set to true by
/// this function if it escaped the peeked character. Otherwise,
/// it's set to false.
///
/// @return peeked character.
char
peek(bool& escaped)
{
if (!buf_.empty())
return buf_.back();
escaped = false;
char c = in_.peek();
if (handle_escape(c, /*peek=*/true))
{
put_back(c);
escaped = true;
}
return c;
}
/// @return the character that is going to be read by the next
/// invocation of read_next_char().
///
/// Note that this function doesn't alter the input stream.
///
/// Also note that this function handles escaping using the '\'
/// (backslash) character.
///
/// @return peeked character.
char
peek()
{
bool escaped = false;
return peek(escaped);
}
/// Get the next character of the input stream.
///
/// This function knows how to handles escaped characters from the
/// input stream.
///
/// @param do_handle_escape if yes, this function handles escaped
/// characters from the input stream.
///
/// @return the next character of the input stream.
char
get(bool do_handle_escape = true)
{
char result = 0;
if (!buf_.empty())
{
result = buf_.back();
buf_.pop_back();
}
else
{
result = in_.get();
if (do_handle_escape)
handle_escape(result);
}
return result;
}
/// Put a character that was read from the input stream, back into
/// that input stream, so that a subsequent call to
/// read_context::get() returns that same character.
///
/// @param c the character to put back into the stream.
void
put_back(char c)
{buf_.push_back(c);}
/// Test if the status of the input stream is good.
///
/// @return true iff the status of the input stream is good.
bool
good() const
{
if (!buf_.empty())
return true;
return in_.good();
}
/// Tests if the input stream has reached end of file.
///
/// @return true iff the input stream has reached end of file.
bool
eof() const
{
if (!buf_.empty())
return false;
return in_.eof();
}
/// Handles the escaping of a character.
///
/// This function must be called whenever the low level character
/// reading function encountered a backslash character ('\'). In
/// that case, this function reads the subsequent characters from
/// the input stream, sees if it needs to escape them and then
/// handles the escaping if need be. Otherwise, it does nothing.
///
/// This is a subroutine of the read_context::get() and
/// read_context::peek() functions.
///
/// @param peek if true, it means this function was called after the
/// caller issued a read_context::peek() call, rather than a
/// read_context::get() call.
///
/// @return true if an escaping took place.
bool
handle_escape(char& c, bool peek = false)
{
bool escaped = false;
char b = c;
if (b == '\\')
{
escaped = true;
b = get(/*escape=*/false);
if (!good())
return escaped;
if (peek)
{
ABG_ASSERT(b == c);
b = get(/*escape=*/false);
if (!good())
return escaped;
}
switch (b)
{
case '0':
case 'a':
case 'b':
case 'r':
// let's replace this by a space
c = ' ';
break;
case 't':
c = '\t';
break;
case '\n':
// continuation line. So we should drop both the backslash
// character and this end-of-line character on the floor
// just like if they never existed.
++cur_column_;
b = get(/*escape=*/false);
if (!good())
return escaped;
c = b;
break;
case '\\':
case ';':
case '#':
case '[':
case ']':
default:
c = b;
break;
}
}
else
c = b;
return escaped;
}
/// Read the next character from the input stream.
///
/// This method updates the current line/column number after looking
/// at the actual char that got read. Note that escaped characters
/// are handled transparently at this point.
///
/// @param c output parameter. This is set by this function to the
/// character that was read. It's set iff the function returned
/// true.
///
/// @return true if the reading went well and if the input stream is
/// in a non-erratic state.
bool
read_next_char(char& c)
{
char b = get();
if (!good())
return false;
c = b;
if (cur_line_ == 0)
cur_line_ = 1;
if (b == '\n')
{
++cur_line_;
cur_column_ = 0;
}
else
++cur_column_;
return true;
}
/// Skip (that is, read characters and drop them on the floor) all
/// the characters up to the next line.
///
/// Note that the new line character (\n' on unices) is skipped as
/// well.
///
/// @return true iff the skipping proceeded successfully and that
/// the input stream is left in a non-erratic state.
bool
skip_line()
{
char c = 0;
for (bool is_ok = read_next_char(c);
is_ok;
is_ok = read_next_char(c))
if (c == '\n')
break;
return (c == '\n' || eof());
}
/// If the current character is a white space, skip it and all the
/// contiguous ones that follow.
///
/// @return true iff the input stream is left in a non-erratic state.
bool
skip_white_spaces()
{
for (char c = peek(); good(); c = peek())
if (char_is_white_space(c))
ABG_ASSERT(read_next_char(c));
else
break;
return good() || eof();
}
/// If the current character is the beginning of a comment, skip
/// (read and drop on the floor) the entire remaining line,
/// including the current character.
///
/// @return true if the input stream is left in a non-erratic state.
bool
skip_comments()
{
for (char c = peek(); good(); c = peek())
if (char_is_comment_start(c))
skip_line();
else
break;
return good() || eof();
}
/// If the current character is either the beginning of a comment or
/// a white space, skip the entire commented line or the subsequent
/// contiguous white spaces.
///
/// @return true iff the stream is left in a non-erratic state.
bool
skip_white_spaces_or_comments()
{
int b = 0;
while (good())
{
b = peek();
if (char_is_white_space(b))
skip_white_spaces();
else if (char_is_comment_start(b))
skip_comments();
else
break;
}
return good() || eof();
}
/// Read a property name.
///
/// @param name out parameter. Is set to the parsed property name,
/// if any. Note that this is set only if the function returned
/// true.
///
/// @return true iff the input stream is left in a non-erratic
/// state.
bool
read_property_name(string& name)
{
char c = peek();
if (!good() || !char_is_property_name_char(c))
return false;
ABG_ASSERT(read_next_char(c));
name += c;
for (c = peek(); good(); c = peek())
{
if (!char_is_property_name_char(c))
break;
ABG_ASSERT(read_next_char(c));
name += c;
}
return true;
}
/// Read a function name.
///
/// @param name the name of the function. This is an output
/// parameter when this puts the function name that was read, iff
/// this function returns true.
///
/// @return true iff the function name was successfully read into @p
/// name.
bool
read_function_name(string& name)
{
char c = peek();
if (!good() || !char_is_function_name_char(c))
return false;
ABG_ASSERT(read_next_char(c));
name += c;
for (c = peek(); good(); c = peek())
{
if (!char_is_function_name_char(c))
break;
ABG_ASSERT(read_next_char(c));
name += c;
}
return true;
}
/// Read a function argument.
///
/// @param argument the argument of the function that was just
/// read. This is an ouput parameter that is set iff the function
/// returns true.
///
/// @return true iff parameter @p argument was successful set.
bool
read_function_argument(string& argument)
{
char c = peek();
if (!good() || !char_is_function_argument_char(c))
return false;
ABG_ASSERT(read_next_char(c));
argument += c;
for (c = peek(); good(); c = peek())
{
if (!char_is_function_argument_char(c))
break;
ABG_ASSERT(read_next_char(c));
argument += c;
}
return true;
}
/// Read a function call expression.
///
/// The expression this function can read has the form:
/// 'foo(bar,baz, blah)'
///
/// @param expr this is an output parameter that is set with the
/// resulting function call expression that was read, iff this
/// function returns true.
///
/// @param return true iff @p expr was successful set with the
/// function call expression that was read.
bool
read_function_call_expr(function_call_expr_sptr& expr)
{
if (!good())
return false;
skip_white_spaces_or_comments();
if (!good())
return false;
string name;
if (!read_function_name(name) || name.empty())
return false;
skip_white_spaces_or_comments();
int b = peek();
if (!good() || b != '(')
return false;
char c = 0;
if (!read_next_char(c))
return false;
ABG_ASSERT(c == '(');
skip_white_spaces_or_comments();
if (!good())
return false;
// Read function call arguments.
vector<string> arguments;
for (;;)
{
if (peek() == ')')
break;
string arg;
if (!read_function_argument(arg))
return true;
skip_white_spaces_or_comments();
if (!good())
return false;
if (peek() == ',')
{
c = 0;
ABG_ASSERT(read_next_char(c) && c == ',');
skip_white_spaces_or_comments();
if (!good())
return false;
}
arguments.push_back(arg);
}
c = 0;
ABG_ASSERT(read_next_char(c) && c == ')');
expr.reset(new function_call_expr(name, arguments));
return true;
}
/// Read a property value.
///
/// @return the property value read.
property_value_sptr
read_property_value()
{
property_value_sptr nil, result;
int b = peek();
if (!good())
return nil;
if (b == '{')
{
if (tuple_property_value_sptr t = read_tuple_property_value())
return t;
return nil;
}
list_property_value_sptr list = read_list_property_value();
if (list->get_content().size() == 1)
result.reset(new string_property_value(list->get_content()[0]));
else
result = list;
return result;
}
/// Reads a string from the input stream.
///
/// A string is just a contiguous set of characters that test
/// positive when passed to
/// read_context::char_is_property_name_char().
///
/// Note that all escaped characters are suitable to be in a string.
///
/// @return the string read.
string
read_string()
{
bool escaped = false;
int b = peek(escaped);
if (!good())
return "";
if (!escaped && char_is_delimiter(b, /*include_white_space=*/false))
// Empty property value. This is accepted.
return "";
string v;
for (b = peek(escaped); good(); b = peek(escaped))
{
// If the current character is not suitable to be a in string,
// then we reached the end of the string. Note that espaced
// characters are always suitable to be a string.
if (!escaped && !char_is_property_value_char(b))
break;
char c = 0;
ABG_ASSERT(read_next_char(c));
v += c;
}
return trim_white_space(v);
}
/// Read a string property value.
///
/// @return the property value that has been parsed.
string_property_value_sptr
read_string_property_value()
{
string_property_value_sptr nil, result;
if (!good())
return nil;
string value = read_string();
result.reset(new string_property_value(value));
return result;
}
/// Read a @ref list_property_value.
///
/// @return the instance of @ref list_property_value read, or nil if
/// none was read.
list_property_value_sptr
read_list_property_value()
{
list_property_value_sptr nil, result;
string str;
vector<string> content;
for (;;)
{
str = read_string();
if (str.empty())
break;
content.push_back(str);
skip_white_spaces();
int b = peek();
if (!good() || b != ',')
break;
skip_white_spaces();
char c = 0;
read_next_char(c);
ABG_ASSERT(c == ',');
}
if (!content.empty())
result.reset(new list_property_value(content));
return result;
}
/// A property value that is a tuple.
///
/// @param tuple the read property tuple value.
///
/// @return true iff the tuple property value could be read
/// correctly.
tuple_property_value_sptr
read_tuple_property_value()
{
tuple_property_value_sptr nil, result;
int b = peek();
if (!good())
return nil;
if (b != '{')
return nil;
char c = 0;
ABG_ASSERT(read_next_char(c));
property_value_sptr value;
vector<property_value_sptr> values;
while (good() && peek() != '}')
{
skip_white_spaces();
if ((value = read_property_value()))
values.push_back(value);
skip_white_spaces();
if (good() && peek() == ',')
{
c = 0;
read_next_char(c);
}
}
b = peek();
if (b != '}')
return nil;
c = 0;
read_next_char(c);
result.reset(new tuple_property_value(values));
return result;
}
/// Read the name of a section.
///
/// @param name out parameter. Is set to the name of the section
/// that was parsed. Note that this is set only if the function
/// returned true.
///
/// @return true if the input stream was left in a non-erratic
/// state.
bool
read_section_name(string& name)
{
int b = peek();
if (!good() || !char_is_section_name_char(b))
return false;
char c = 0;
ABG_ASSERT(read_next_char(c) || char_is_section_name_char(b));
name += c;
for (b = peek(); good(); b = peek())
{
if (!char_is_section_name_char(b))
break;
ABG_ASSERT(read_next_char(c));
name += c;
}
return true;
}
/// Read a property (<name> = <value>).
///
/// @return the resulting pointer to property iff one could be
/// parsed.
property_sptr
read_property()
{
property_sptr nil;
string name;
if (!read_property_name(name))
return nil;
skip_white_spaces();
property_sptr result;
char c = peek();
if (c == '=')
{
ABG_ASSERT(read_next_char(c));
ABG_ASSERT(c == '=');
skip_white_spaces();
}
else
{
property_sptr empty_value_property(new simple_property(name));
return empty_value_property;
}
if (!good())
return nil;
property_value_sptr value = read_property_value();
if (!value)
return nil;
if (tuple_property_value_sptr tv = is_tuple_property_value(value))
result.reset(new tuple_property(name, tv));
else if (list_property_value_sptr lv = is_list_property_value(value))
result.reset(new list_property(name, lv));
else if (string_property_value_sptr sv = is_string_property_value(value))
result.reset(new simple_property(name, sv));
else
// This new kind of property is not yet supported!
std::abort();
return result;
}
/// Read an ini section.
///
/// @return a pointer to a section iff it could be successfully
/// parsed.
config::section_sptr
read_section()
{
config::section_sptr nil;
int b = peek();
if (!good())
return nil;
char c = 0;
if (b == '[')
ABG_ASSERT(read_next_char(c) && c == '[');
string name;
if (!read_section_name(name))
return nil;
if (!skip_white_spaces())
return nil;
if (! read_next_char(c) || c != ']')
return nil;
if (!skip_white_spaces_or_comments())
return nil;
config::properties_type properties;
while (property_sptr prop = read_property())
{
properties.push_back(prop);
skip_white_spaces_or_comments();
}
if (!properties.empty())
{
config::section_sptr section(new config::section(name, properties));
return section;
}
return nil;
}
};//end struct read_context
// </read_context stuff>
// <config stuff>
class config::priv
{
string path_;
sections_type sections_;
public:
friend class config;
priv()
{}
priv(const string& path,
sections_type& sections)
: path_(path),
sections_(sections)
{}
};
/// @param path the path to the config file.
///
/// @param sections the sections of the config file.
config::config(const string& path,
sections_type& sections)
: priv_(new priv(path, sections))
{}
config::config()
: priv_(new priv)
{}
config::~config()
{}
/// @return the path to the config file.
const string&
config::get_path() const
{return priv_->path_;}
/// Set the path to the config file.
///
/// @param the new path to the config file.
void
config::set_path(const string& path)
{priv_->path_ = path;}
/// @return the sections of the config file.
const config::sections_type&
config::get_sections() const
{return priv_->sections_;}
/// Set new sections to the ini config
///
/// @param sections the new sections to set.
void
config::set_sections(const sections_type& sections)
{priv_->sections_ = sections;}
// </config stuff>
// <config reader stuff>
/// Parse the sections of an *.ini file.
///
/// @param input the input stream to parse the ini file from.
///
/// @param section out parameter. This is set to the vector of
/// sections that have been parsed from the input stream.
///
/// @return true upon successful completion and if if the stream is
/// left in a non-erratic state.
bool
read_sections(std::istream& input,
config::sections_type& sections)
{
read_context ctxt(input);
while (input.good())
{
ctxt.skip_white_spaces_or_comments();
if (config::section_sptr section = ctxt.read_section())
sections.push_back(section);
else
break;
}
return input.good() || input.eof();
}
/// Parse the sections of an *.ini file.
///
/// @param path the path of the ini file to parse.
///
/// @param section out parameter. This is set to the vector of
/// sections that have been parsed from the input stream.
///
/// @return true upon successful completion and if if the stream is
/// left in a non-erratic state.
bool
read_sections(const string& path,
config::sections_type& sections)
{
std::ifstream in(path.c_str(), std::ifstream::binary);
if (!in.good())
return false;
bool is_ok = read_sections(in, sections);
in.close();
return is_ok;
}
/// Parse an ini config file from an input stream.
///
/// @param input the input stream to parse the ini config file from.
///
/// @return true upon successful parsing.
bool
read_config(istream& input,
config& conf)
{
config::sections_type sections;
if (!read_sections(input, sections))
return false;
conf.set_sections(sections);
return true;
}
/// Parse an ini config file from a file on disk.
///
/// @param path the path to the ini file to parse.
///
/// @param conf the resulting config file to populate as a result of
/// the parsing. This is populated iff the function returns true.
///
/// @return true upon succcessful completion.
bool
read_config(const string& path,
config& conf)
{
config::sections_type sections;
if (!read_sections(path, sections))
return false;
conf.set_path(path);
conf.set_sections(sections);
return true;
}
/// Parse an ini config file from an input stream.
///
/// @return a shared pointer to the resulting config, or nil if it
/// couldn't be parsed.
config_sptr
read_config(std::istream& input)
{
config_sptr c(new config);
if (!read_config(input, *c))
return config_sptr();
return c;
}
/// Parse an ini config file from an on-disk file.
///
/// @return a shared pointer to the resulting config, or nil if it
/// couldn't be parsed.
config_sptr
read_config(const string& path)
{
config_sptr c(new config);
if (!read_config(path, *c))
return config_sptr();
return c;
}
// <config reader stuff>
// <config writer stuff>
/// Serialize the value of a property to a string.
///
/// @param prop the property which value to serialize.
///
/// @return the string that represents the value of @p prop.
static string
write_property_value(const property_sptr& prop)
{
string result;
if (simple_property_sptr simple_prop = is_simple_property(prop))
{
if (!simple_prop->has_empty_value())
result = simple_prop->get_value()->as_string();
}
else if (list_property_sptr list_prop = is_list_property(prop))
result = list_prop->get_value()->as_string();
else if (tuple_property_sptr tuple_prop = is_tuple_property(prop))
result = tuple_prop->get_value()->as_string();
else
// This new kind of property is not yet supported!
abort();
return result;
}
/// Serialize an ini property to an output stream.
///
/// @param prop the property to serialize to the output stream.
///
/// @param out the output stream to serialize to.
///
/// @return true if the ouput stream is left in a non-erratic state.
static bool
write_property(const property_sptr& prop,
std::ostream& out)
{
out << prop->get_name();
string value = write_property_value(prop);
if (!value.empty())
out << " = " << write_property_value(prop);
return out.good();
}
/// Serialize an ini section to an output stream.
///
/// @param section the ini section to serialize.
///
/// @param out the output stream to serialize the section to.
static bool
write_section(const config::section& section,
std::ostream& out)
{
out << "[" << section.get_name() << "]\n";
for (config::properties_type::const_iterator i =
section.get_properties().begin();
i != section.get_properties().end();
++i)
{
out << " ";
write_property(*i, out);
out << "\n";
}
return out.good();
}
/// Serialize a vector of sections that make up an ini config file to
/// an output stream.
///
/// Note that an ini config is just a collection of sections.
///
/// @param sections the vector of sections to serialize.
///
/// @param out the output stream.
///
/// @return true if the output stream is left in a non-erratic state.
bool
write_sections(const config::sections_type& sections,
std::ostream& out)
{
for (config::sections_type::const_iterator i = sections.begin();
i != sections.end();
++i)
{
write_section(**i, out);
out << "\n";
}
return out.good();
}
/// Serialize a vector of sections that make up an ini config to a
/// file.
///
/// @param sections the vector of sections to serialize.
///
/// @param out the output stream.
///
/// @return true if the output stream is left in a non-erratic state.
bool
write_sections(const config::sections_type& sections,
const string& path)
{
std::ofstream f(path.c_str(), std::ofstream::binary);
if (!f.good())
return false;
bool is_ok = write_sections(sections, f);
f.close();
return is_ok;
}
/// Serialize an instance of @ref config to an output stream.
///
/// @param conf the instance of @ref config to serialize.
///
/// @param output the output stream to serialize @p conf to.
///
/// @return true upon successful completion.
bool
write_config(const config& conf,
std::ostream& output)
{
if (!write_sections(conf.get_sections(), output))
return false;
return true;
}
/// Serialize an instance of @ref conf to an on-disk file.
///
/// @param conf the instance of @ref config to serialize.
///
/// @param path the path to the on-disk file to serialize to.
///
/// @return true upon successful completion.
bool
write_config(const config& conf,
const string& path)
{
if (!write_sections(conf.get_sections(), path))
return false;
return true;
}
// </config writer stuff>
// <function_call_expr stuff>
/// The private data type of @ref function_call_expr.
struct function_call_expr::priv
{
string name_;
vector<string> arguments_;
priv()
{}
priv(const string& name,
const vector<string>& arguments)
: name_(name),
arguments_(arguments)
{}
}; // end struct function_call_expr::priv
/// Constructor for the @ref function_call_expr type.
///
/// @param name the name of the function being called.
///
/// @param args a vector of the arguements of the function being
/// called.
function_call_expr::function_call_expr(const string& name,
const vector<string>& args)
: priv_(new priv(name, args))
{}
/// Getter of the name of the function being called.
///
/// @return the name of the function being called.
const string&
function_call_expr::get_name() const
{return priv_->name_;}
/// Getter for the arguments of the function call expression.
///
/// That is, a getter for the arguments of the function call.
///
/// @return the operands of the function call expression.
const vector<string>&
function_call_expr::get_arguments() const
{return priv_->arguments_;}
/// Getter for the arguments of the function call expression.
///
/// That is, a getter for the arguments of the function call.
///
/// @return the operands of the function call expression.
vector<string>&
function_call_expr::get_arguments()
{return priv_->arguments_;}
/// Read a function call expression and build its representation.
///
/// @param input the input stream where to read the function call
/// expression from.
///
/// @param expr the expression resulting from the parsing. This is an
/// output parameter that is set iff this function returns true.
///
/// @return true iff the parameter @p expr is successfully set with
/// the resulting of parsing the @p input.
bool
read_function_call_expr(std::istream& input,
function_call_expr_sptr& expr)
{
read_context ctxt(input);
return ctxt.read_function_call_expr(expr);
}
/// Read a function call expression and build its representation.
///
/// @param input a string where to read the function call expression
/// from.
///
/// @param expr the expression resulting from the parsing. This is an
/// output parameter that is set iff this function returns true.
///
/// @return true iff the parameter @p expr is successfully set with
/// the resulting of parsing the @p input.
bool
read_function_call_expr(const string& input,
function_call_expr_sptr& expr)
{
std::istringstream in(input);
return read_function_call_expr(in, expr);
}
/// Read a function call expression and build its representation.
///
/// @param input a string where to read the function call expression
/// from.
///
/// @return the representation of the expression resulting from the
/// parsing.
function_call_expr_sptr
read_function_call_expr(const string& input)
{
function_call_expr_sptr expr;
read_function_call_expr(input, expr);
return expr;
}
// </function_call_expr stuff>
}// end namespace ini
}// end namespace abigail