2005-01-07 00:35:17 +00:00
|
|
|
/** @file
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
$Date$
|
|
|
|
$Author$
|
|
|
|
|
|
|
|
@copy © Marc Wäckerlin
|
|
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
|
|
|
|
$Log$
|
2005-01-28 07:49:32 +00:00
|
|
|
Revision 1.2 2005/01/28 07:49:32 marc
|
|
|
|
Save configuration using file.hpp
|
|
|
|
|
2005-01-07 00:35:17 +00:00
|
|
|
Revision 1.1 2005/01/07 00:31:38 marc
|
|
|
|
initial version
|
|
|
|
|
|
|
|
|
2005-01-28 07:49:32 +00:00
|
|
|
*/
|
2005-01-07 00:35:17 +00:00
|
|
|
#ifndef __MRW_CONFIGFILE_HPP__
|
|
|
|
#define __MRW_CONFIGFILE_HPP__
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
namespace mrw {
|
|
|
|
|
|
|
|
/** @defgroup config Configuration File Handler
|
|
|
|
|
2005-01-28 07:49:32 +00:00
|
|
|
@pre #include <mrw/configfile.hpp>
|
|
|
|
|
2005-01-07 00:35:17 +00:00
|
|
|
Read configuration parameters from a file.
|
|
|
|
|
|
|
|
@section configSyntax Configuration File Syntax
|
|
|
|
|
|
|
|
@subsection configSyntaxSection Sections: [Section]
|
|
|
|
|
|
|
|
Syntax (regular expression):
|
|
|
|
<code>\\[([^[=#]*)\\]</code>
|
|
|
|
|
|
|
|
A configuration file consists of none or more sections. The
|
|
|
|
sections start with tag <code>[Section Name]</code>, where
|
|
|
|
<code>Section Name</code> is the name of the section.
|
|
|
|
The default section has an empty name.
|
|
|
|
|
|
|
|
@subsection configSyntaxVariables Variables: name = value
|
|
|
|
|
|
|
|
Syntax (regular expression):
|
|
|
|
<code>^[^[\\n\\t ]*([^[=#\\n]]+)[\\n\\t ]*=[\\n\\t ]*([^[=#]]*)[\\n\\t ]*</code>
|
|
|
|
or
|
|
|
|
<code>^[^[\\n\\t ]*([^[=#\\n]]+)[\\n\\t ]*=[\\n\\t ]*("[^"]]*")[\\n\\t ]*</code>
|
|
|
|
or
|
|
|
|
<code>^[^[\\n\\t ]*([^[=#\\n]]+)[\\n\\t ]*=[\\n\\t ]*('[^']]*')[\\n\\t ]*</code>
|
|
|
|
|
|
|
|
In the section, there are variable definitions in the form of
|
|
|
|
<code>name=value</code>, where <code>name</code> is the name of
|
|
|
|
the variable and <code>value</code> is it's contents. White
|
|
|
|
spaces at the begin or end of the name and the value are
|
|
|
|
stripped. Whitespaces inside a name or value remain
|
|
|
|
unchanged. Please note that a variable name starts at the begoin
|
|
|
|
of line and ends before the equal sign, while the contents
|
|
|
|
starts after the equal sign and ends before the next token
|
|
|
|
starts. If you need a token inside a value, or a variable must
|
|
|
|
start or end with white spaces, you can enclose it in either
|
|
|
|
<code>"</code> or <code>'</code> quotes. If a value is quoted
|
|
|
|
and the quoting is not terminated, then the contents is taken up
|
|
|
|
to the end of file. Please note that the same quote must not
|
|
|
|
occur inside the value. All possible tokens are: <code>[</code>,
|
|
|
|
<code>#</code> and <code>=</code>. All other characters are not
|
|
|
|
treated in a special way.
|
|
|
|
|
|
|
|
@subsection configSyntaxComments Comments: #
|
|
|
|
|
|
|
|
Comments start with <code>#</code> and end at the end of the
|
|
|
|
line.
|
|
|
|
|
|
|
|
@subsection configSyntaxSpecial Special Characters \\n, [, ], #, =, ", '
|
|
|
|
|
|
|
|
I have chosen a syntax that is downwards compatible with the
|
|
|
|
common Unix configuration file syntax, but with a grammar as
|
|
|
|
loose as possible and with as few restrictions as
|
|
|
|
possible. Special characters have only special meanings in
|
|
|
|
certain contexts. In other contexts, they can be used with no
|
|
|
|
restrictions.
|
|
|
|
|
|
|
|
The following characters are treated in a special way:
|
|
|
|
|
|
|
|
- <code>[</code> and <code>]</code> enclose a section name. A
|
|
|
|
section name may contain any arbitrary character, including new
|
|
|
|
line and white spaces, except a <code>]</code>.
|
|
|
|
|
|
|
|
- <code>\\n</code> (begin of line or file) and <code>=</code>
|
|
|
|
enclose a variable name. In other circumstances (except in
|
|
|
|
comments), a new line has no special meaning. A <code>=</code>
|
|
|
|
inside a value is allowed only if the value is quoted. A
|
|
|
|
variable name may contain any arbitrary character, including
|
|
|
|
white spaces, except a new line or <code>=</code>.
|
|
|
|
|
|
|
|
- <code>=</code> and one out of <code>[</code>, <code>#</code>
|
|
|
|
and the begin of a line that contains <code>=</code> enclose a
|
|
|
|
variable contents if the contents is not quoted. A variable's
|
|
|
|
contents may contain any arbitrary character, including new line
|
|
|
|
and white spaces, except a <code>]</code>, <code>#</code> or the
|
|
|
|
begin of a line that contains <code>=</code>.
|
|
|
|
|
|
|
|
- <code>"</code> or <code>'</code> may enclose a variables
|
|
|
|
contents. These characters have only a special meaning, if a
|
|
|
|
variable's contents starts with one of them. Then the variable's
|
|
|
|
contents may contain any arbitrary character, except the same
|
|
|
|
quote itself, which marks the end of the contents.
|
|
|
|
|
|
|
|
- <code>#</code> and <code>\\n</code> (end of line or file)
|
|
|
|
enclose a comment. A variable name may contain any arbitrary
|
|
|
|
character, including white spaces, except a new line.
|
|
|
|
|
|
|
|
@subsection configSyntaxRestrictions Restrictions
|
|
|
|
|
|
|
|
@attention Due to the actual syntax, it is impossible that a
|
|
|
|
variable's contents contains the following combination:
|
|
|
|
<code>"</code> @b and <code>'</code> @b and one out of
|
|
|
|
<code>[</code>, <code>=</code> and <code>#</code>
|
|
|
|
|
|
|
|
@attention If a variable's contents contains heading or trailing
|
|
|
|
white spaces or one out of <code>[</code>, <code>=</code> and
|
|
|
|
<code>#</code>, then it must be quoted either with
|
|
|
|
<code>"</code> or with <code>'</code>.
|
|
|
|
|
|
|
|
@section configFile Example File
|
|
|
|
|
|
|
|
@subsection configFileSamp1 Nice Example
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
[Section 1]
|
|
|
|
name1 = value1
|
|
|
|
name2 = value2
|
|
|
|
[Section 2]
|
|
|
|
name1 = value1
|
|
|
|
name2 = value2
|
|
|
|
@endverbatim
|
|
|
|
|
|
|
|
@subsection configFileSamp2 Enhanced Example
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
# this is in the global section, named ""
|
|
|
|
this is a variable name = this is a variable contents
|
|
|
|
|
|
|
|
[Global] # start of section "Global"
|
|
|
|
|
2005-01-28 07:49:32 +00:00
|
|
|
multi line text = this is a text
|
|
|
|
that is longer than one
|
|
|
|
line!
|
2005-01-07 00:35:17 +00:00
|
|
|
|
|
|
|
1234 = "var=17" # if you need an equal inside a text, enclose it
|
2005-01-28 07:49:32 +00:00
|
|
|
# either in " or in '
|
2005-01-07 00:35:17 +00:00
|
|
|
@endverbatim
|
|
|
|
|
|
|
|
@section configFileCode Code Sample
|
|
|
|
|
|
|
|
@code
|
|
|
|
try {
|
|
|
|
const char* home(getenv("HOME"));
|
|
|
|
mrw::ConfigFileReader config(home?home+"/.myprogram":".myprogram");
|
|
|
|
std::string userName(config("User Info", "Name"));
|
|
|
|
} catch (...) { // file read or evaluation error
|
|
|
|
}
|
|
|
|
@endcode
|
2005-01-28 07:49:32 +00:00
|
|
|
*/
|
2005-01-07 00:35:17 +00:00
|
|
|
//@{
|
|
|
|
|
2005-01-28 07:49:32 +00:00
|
|
|
//============================================================================
|
|
|
|
/** @brief Parse configuration file and access the contents.
|
|
|
|
|
|
|
|
Parse a configuration file that consists of sections containing
|
2005-01-07 00:35:17 +00:00
|
|
|
variables with values. This class does not store anything in the
|
|
|
|
file, even if variables have changed.
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
@see If you want %to save changes %to the file, use @ref ConfigFileWriter.
|
|
|
|
@see For details, see @ref configSyntax.
|
|
|
|
*/
|
2005-01-07 00:35:17 +00:00
|
|
|
class ConfigFileReader {
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
//............................................................... typedefs
|
2005-01-07 00:35:17 +00:00
|
|
|
public:
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
//------------------------------------------------------------------ Value
|
|
|
|
/// A configuration file value.
|
|
|
|
class Value {
|
|
|
|
|
|
|
|
//............................................................ methods
|
|
|
|
public:
|
|
|
|
|
|
|
|
/** @brief Get the value string.
|
|
|
|
|
|
|
|
Get the value string. Trailing and leading white spaces have
|
|
|
|
been truncated. If the variable was quoted, quotes have been
|
|
|
|
removed.
|
|
|
|
|
|
|
|
@return the variable's value as string
|
|
|
|
*/
|
|
|
|
operator const std::string&() const throw() {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @brief Get the value string.
|
|
|
|
|
|
|
|
Alternative access to the variable's contents. Trailing and
|
|
|
|
leading white spaces have been truncated. If the variable
|
|
|
|
was quoted, quotes have been removed.
|
|
|
|
|
|
|
|
@return the variable's value as string
|
|
|
|
*/
|
|
|
|
const std::string& operator()() const throw() {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @brief Assign a new value.
|
|
|
|
|
|
|
|
Assign a new value. The new value is lost if you use the
|
|
|
|
ConfigFileReader, but it is stored in the destructor, if you
|
|
|
|
use the ConfigFileWriter instead.
|
|
|
|
*/
|
|
|
|
Value& operator=(const std::string& newValue) throw() {
|
|
|
|
changed = true;
|
|
|
|
value = newValue;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compare the value to a string.
|
|
|
|
bool operator==(const std::string& o) const throw() {
|
|
|
|
return o==value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compare a string to the value.
|
|
|
|
friend bool operator==(const std::string& o, const Value& v) throw() {
|
|
|
|
return v==o;
|
|
|
|
}
|
|
|
|
|
|
|
|
//............................................................ methods
|
|
|
|
protected:
|
|
|
|
|
|
|
|
friend class ConfigFileReader;
|
|
|
|
friend class ConfigFileWriter;
|
|
|
|
|
|
|
|
Value(const std::string& v, std::string::size_type s,
|
|
|
|
std::string::size_type e) throw():
|
|
|
|
value(v), changed(false), start(s), end(e) {}
|
|
|
|
|
|
|
|
Value(const std::string& v) throw():
|
|
|
|
value(v), changed(true), start(0), end(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
//.......................................................... variables
|
|
|
|
protected:
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
bool changed;
|
|
|
|
std::string::size_type start;
|
|
|
|
std::string::size_type end;
|
|
|
|
|
|
|
|
//............................................................ methods
|
|
|
|
private:
|
|
|
|
|
|
|
|
Value(); // not implemented, must be initialized
|
|
|
|
Value& operator=(const Value&); // not implemented, no assignment
|
|
|
|
|
|
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//................................................................ methods
|
|
|
|
public:
|
|
|
|
|
|
|
|
/** @brief Uninitialized construction.
|
|
|
|
|
|
|
|
Uninitialized construction. Use this constructor, if you
|
|
|
|
want to call @ref load() later.
|
|
|
|
*/
|
|
|
|
ConfigFileReader() throw() {}
|
|
|
|
|
|
|
|
/** @brief Parse a file at construction.
|
|
|
|
|
|
|
|
This should be the normal case: load and parse the file at
|
|
|
|
construction.
|
|
|
|
|
|
|
|
@param filename the name of the file to read
|
|
|
|
@throw mrw::invalid_argument
|
|
|
|
- if the file does not exist
|
|
|
|
- if reading the file fails
|
|
|
|
- if there is an unterminated section name in the file
|
|
|
|
- if there is an empty variable name in the file
|
|
|
|
*/
|
|
|
|
ConfigFileReader(const std::string& filename) throw(std::exception):
|
|
|
|
_filename(filename) {
|
|
|
|
reload();
|
2005-01-07 00:35:17 +00:00
|
|
|
}
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
/// Copy construct from other ConfigFileReader.
|
|
|
|
ConfigFileReader(const ConfigFileReader& o) throw():
|
|
|
|
_filename(o._filename), _values(o._values) {
|
2005-01-07 00:35:17 +00:00
|
|
|
}
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
virtual ~ConfigFileReader() throw() {}
|
|
|
|
|
|
|
|
/// Copy from other ConfigFileReader.
|
|
|
|
ConfigFileReader& operator=(const ConfigFileReader& o) {
|
|
|
|
_filename = o._filename;
|
|
|
|
_values = o._values;
|
2005-01-07 00:35:17 +00:00
|
|
|
return *this;
|
|
|
|
}
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
/** @brief Access a value read from the file.
|
|
|
|
|
|
|
|
Access a value read from the file.
|
|
|
|
|
|
|
|
@param section the section in the configuration file
|
|
|
|
@param name the variable name
|
|
|
|
@param default_ the default value that is set, if the variable is
|
|
|
|
not configured
|
|
|
|
@return the Value you are looking for
|
|
|
|
*/
|
|
|
|
Value& operator()(const std::string& section, const std::string& name,
|
|
|
|
const std::string& default_)
|
|
|
|
throw(std::bad_exception);
|
|
|
|
|
|
|
|
/** @brief Load and parse a new file.
|
|
|
|
|
|
|
|
Load and parse a new file.
|
|
|
|
|
|
|
|
@param filename the name of the file to load
|
|
|
|
@throw mrw::invalid_argument
|
|
|
|
- if the file does not exist
|
|
|
|
- if reading the file fails
|
|
|
|
- if there is an unterminated section name in the file
|
|
|
|
- if there is an empty variable name in the file
|
|
|
|
*/
|
|
|
|
ConfigFileReader& load(const std::string& filename) throw(std::exception);
|
|
|
|
|
|
|
|
/** @brief Reload the last file again.
|
|
|
|
|
|
|
|
Reload the last file again.
|
|
|
|
|
|
|
|
@throw mrw::invalid_argument
|
|
|
|
- if the file does not exist
|
|
|
|
- if reading the file fails
|
|
|
|
- if there is an unterminated section name in the file
|
|
|
|
- if there is an empty variable name in the file
|
|
|
|
*/
|
|
|
|
virtual ConfigFileReader& reload() throw(std::exception);
|
|
|
|
|
|
|
|
//............................................................... typedefs
|
2005-01-07 00:35:17 +00:00
|
|
|
protected:
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
typedef std::map< std::string, std::map<std::string, Value> > Values;
|
|
|
|
typedef std::map<std::string, std::string::size_type> Sections;
|
|
|
|
|
|
|
|
//.............................................................. variables
|
|
|
|
protected:
|
|
|
|
|
|
|
|
std::string _filename;
|
|
|
|
Values _values;
|
|
|
|
Sections _sections;
|
|
|
|
|
|
|
|
//................................................................ methods
|
|
|
|
protected:
|
|
|
|
|
|
|
|
ConfigFileReader& parse(const std::string& file) throw(std::exception);
|
|
|
|
|
|
|
|
std::string parseSection(const std::string& file,
|
|
|
|
std::string::size_type& pos)
|
|
|
|
const throw(std::exception);
|
|
|
|
|
|
|
|
Values::mapped_type::value_type parseValue(const std::string& file,
|
|
|
|
std::string::size_type& pos)
|
|
|
|
const throw(std::exception);
|
|
|
|
|
|
|
|
std::string parseComment(const std::string& file,
|
|
|
|
std::string::size_type& pos)
|
|
|
|
const throw(std::bad_exception);
|
2005-01-07 00:35:17 +00:00
|
|
|
};
|
|
|
|
|
2005-01-28 07:49:32 +00:00
|
|
|
//============================================================================
|
2005-01-07 00:35:17 +00:00
|
|
|
/** @brief Parse configuration file and offer read / write
|
|
|
|
access to the contents.
|
|
|
|
|
|
|
|
Parse a configuration file that consists of sections containing
|
|
|
|
variables with values. This class stores the file in the destructor
|
|
|
|
if variables have changed.
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
@see If you want %to leave the file untouched, use @ref ConfigFileReader.
|
|
|
|
@see For details, see @ref configSyntax.
|
|
|
|
*/
|
2005-01-07 00:35:17 +00:00
|
|
|
class ConfigFileWriter: virtual public ConfigFileReader {
|
2005-01-28 07:49:32 +00:00
|
|
|
|
|
|
|
//................................................................ methods
|
|
|
|
public:
|
|
|
|
|
|
|
|
/** @brief Uninitialized construction.
|
|
|
|
|
|
|
|
@copydoc ConfigFileReader()
|
|
|
|
*/
|
|
|
|
ConfigFileWriter() throw() {}
|
|
|
|
|
|
|
|
/** @brief Parse a file at construction.
|
|
|
|
|
|
|
|
@copydoc ConfigFileReader(const std::string&)
|
|
|
|
*/
|
|
|
|
ConfigFileWriter(const std::string& filename) throw(std::exception) {
|
|
|
|
load(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copy construct from other ConfigFileWriter.
|
|
|
|
ConfigFileWriter(const ConfigFileWriter& o) throw():
|
|
|
|
ConfigFileReader(o) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The file is stored at destruction.
|
|
|
|
virtual ~ConfigFileWriter() throw() {
|
|
|
|
save();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copy from other ConfigFileWriter.
|
|
|
|
ConfigFileWriter& operator=(const ConfigFileWriter& o) {
|
|
|
|
ConfigFileReader::operator=(o);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GNU g++ prior to 3.4 does not implement covariant returns
|
|
|
|
# if __GNUC__ == 3 && __GNUC_MINOR__ < 4
|
|
|
|
/** @brief Reload the last file again.
|
|
|
|
|
|
|
|
@copydoc mrw::ConfigFileReader::reload()
|
|
|
|
|
|
|
|
@bug Bug in GNU g++ compiler:
|
|
|
|
return type should be <code>ConfigFileWriter&</code>,
|
|
|
|
but the compiler sais:<br>
|
|
|
|
<code>../mrw/configfile.hpp:295:
|
|
|
|
sorry, unimplemented: adjusting pointers for
|
|
|
|
covariant returns</code><br>
|
|
|
|
The problem is fixed since GNU g++ 3.4, so the signature
|
|
|
|
will be changed as soon as you upgrade to the new compiler
|
|
|
|
version.
|
|
|
|
*/
|
|
|
|
virtual ConfigFileReader& reload() throw(std::exception);
|
|
|
|
# else
|
|
|
|
/** @brief Reload the last file again.
|
|
|
|
|
|
|
|
@copydoc mrw::ConfigFileReader::reload()
|
|
|
|
*/
|
|
|
|
virtual ConfigFileWriter& reload() throw(std::exception);
|
|
|
|
# endif
|
|
|
|
|
|
|
|
/** @brief Saves changes back to the file.
|
|
|
|
|
|
|
|
All changed parameters are stored in the configuration
|
|
|
|
file. The rest of the file is left untouched.
|
|
|
|
*/
|
|
|
|
ConfigFileWriter& save() throw(std::exception);
|
|
|
|
|
|
|
|
//.............................................................. variables
|
|
|
|
protected:
|
|
|
|
|
|
|
|
std::string _file;
|
|
|
|
|
2005-01-07 00:35:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//@}
|
|
|
|
}
|
|
|
|
#endif
|