C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 lines
14 KiB

20 years ago
/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
Revision 1.1 2005/01/07 00:31:38 marc
initial version
*/
#ifndef __MRW_CONFIGFILE_HPP__
#define __MRW_CONFIGFILE_HPP__
#include <string>
#include <map>
namespace mrw {
/** @defgroup config Configuration File Handler
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&nbsp;Name]</code>, where
<code>Section&nbsp;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&nbsp;]*([^[=#\\n]]+)[\\n\\t&nbsp;]*=[\\n\\t&nbsp;]*([^[=#]]*)[\\n\\t&nbsp;]*</code>
or
<code>^[^[\\n\\t&nbsp;]*([^[=#\\n]]+)[\\n\\t&nbsp;]*=[\\n\\t&nbsp;]*("[^"]]*")[\\n\\t&nbsp;]*</code>
or
<code>^[^[\\n\\t&nbsp;]*([^[=#\\n]]+)[\\n\\t&nbsp;]*=[\\n\\t&nbsp;]*('[^']]*')[\\n\\t&nbsp;]*</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"
multi line text = this is a text
that is longer than one
line!
1234 = "var=17" # if you need an equal inside a text, enclose it
# either in " or in '
@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
*/
//@{
/// Parse configuration file and access the contents.
/** Parse a configuration file that consists of sections containing
variables with values. This class does not store anything in the
file, even if variables have changed.
@see If you want %to save changes %to the file, use
@ref ConfigFileWriter.
@see For details, see @ref configSyntax. */
class ConfigFileReader {
public:
/// A configuration file value.
class Value {
public:
/// 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;
}
/// 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;
}
/// 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;
}
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) {
}
std::string value;
bool changed;
std::string::size_type start;
std::string::size_type end;
private:
Value(); // not implemented, must be initialized
Value& operator=(const Value&); // not implemented, no assignment
};
/// Uninitialized construction.
/** Uninitialized construction. Use this constructor, if you want
to call @ref load() later. */
ConfigFileReader() throw() {}
/// 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();
}
/// Copy construct from other ConfigFileReader.
ConfigFileReader(const ConfigFileReader& o) throw():
_filename(o._filename), _values(o._values) {
}
virtual ~ConfigFileReader() throw() {}
/// Copy from other ConfigFileReader.
ConfigFileReader& operator=(const ConfigFileReader& o) {
_filename = o._filename;
_values = o._values;
return *this;
}
/// 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);
/// Load and parse a new file.
/** Load and parse a new file.
@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);
/// 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);
protected:
typedef std::map< std::string, std::map<std::string, Value> > Values;
typedef std::map<std::string, std::string::size_type> Sections;
std::string readFile() const throw(std::exception);
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);
std::string _filename;
Values _values;
Sections _sections;
};
/** @brief Parse configuration file and offer read&nbsp;/ 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.
@see If you want %to leave the file untouched, use
@ref ConfigFileReader.
@see For details, see @ref configSyntax. */
class ConfigFileWriter: virtual public ConfigFileReader {
public:
/// Uninitialized construction.
/** @copydoc ConfigFileReader() */
ConfigFileWriter() throw() {}
/// Parse a file at construction.
/** @copydoc ConfigFileReader(const std::string& filename) */
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
/// 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
/// Reload the last file again.
/** @copydoc mrw::ConfigFileReader::reload() */
virtual ConfigFileWriter& reload() throw(std::exception);
# endif
/// 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);
protected:
std::string _file;
};
//@}
}
#endif