parent
cabcaa0475
commit
6d5fc2ff77
8 changed files with 723 additions and 1 deletions
@ -0,0 +1,202 @@ |
||||
/** @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 |
||||
|
||||
|
||||
1 2 3 4 5 6 7 8 |
||||
5678901234567890123456789012345678901234567890123456789012345678901234567890 |
||||
*/ |
||||
|
||||
#include <mrw/configfile.hpp> |
||||
#include <mrw/exception.hpp> |
||||
#include <mrw/string.hpp> |
||||
#include <fstream> |
||||
|
||||
mrw::ConfigFileReader::Value& |
||||
mrw::ConfigFileReader::operator()(const std::string& section, |
||||
const std::string& name, |
||||
const std::string& default_) |
||||
throw(std::bad_exception) { |
||||
_values[section].insert(std::make_pair(name, Value(default_))); |
||||
return _values[section].find(name)->second; |
||||
} |
||||
|
||||
mrw::ConfigFileReader& mrw::ConfigFileReader::load(const std::string& filename) |
||||
throw(std::exception) { |
||||
_filename = filename; |
||||
return reload(); |
||||
} |
||||
|
||||
mrw::ConfigFileReader& mrw::ConfigFileReader::reload() throw(std::exception) { |
||||
return parse(readFile()); |
||||
} |
||||
|
||||
std::string mrw::ConfigFileReader::readFile() const throw(std::exception) { |
||||
std::string fileContents; // declaration as first allows optimization
|
||||
std::ifstream file(_filename.c_str()); |
||||
if (!file) |
||||
throw mrw::invalid_argument("Error file not found: '"+_filename+"'"); |
||||
file.seekg(0, std::ios::end); |
||||
int size(file.tellg()); |
||||
fileContents.resize(size); // make buffer long enough to store all
|
||||
file.seekg(0, std::ios::beg); |
||||
file.read(fileContents.begin().operator->(), size); // hack to get the buffer
|
||||
if (!file.good() && file.eof()) |
||||
throw mrw::invalid_argument("Error reading file: '"+_filename+"'");
|
||||
return fileContents; |
||||
} |
||||
|
||||
#include <iostream> |
||||
using namespace std; |
||||
mrw::ConfigFileReader& mrw::ConfigFileReader::parse(const std::string& file) |
||||
throw(std::exception) { |
||||
_values.erase(_values.begin(), _values.end()); |
||||
std::string section; |
||||
for (std::string::size_type pos(0); |
||||
(pos=file.find_first_of("[=#", pos))!=std::string::npos; ++pos) { |
||||
switch (file[pos]) { |
||||
case '[': _sections[section]=pos; section=parseSection(file, pos); break; |
||||
case '=': _values[section].insert(parseValue(file, pos)); break; |
||||
case '#': parseComment(file, pos); break; |
||||
}; |
||||
} |
||||
_sections[section]=file.size(); |
||||
return *this; |
||||
} |
||||
|
||||
std::string mrw::ConfigFileReader::parseSection(const std::string& file, |
||||
std::string::size_type& pos) |
||||
const throw(std::exception) { |
||||
std::string::size_type start(pos); |
||||
if ((pos=file.find(']', pos))==std::string::npos) |
||||
throw mrw::invalid_argument |
||||
("section not terminated in file '"+_filename+"'\n" |
||||
"environment is: "+file.substr(start-10>0?start-10:0)); |
||||
return file.substr(start+1, pos-start-1); |
||||
} |
||||
|
||||
mrw::ConfigFileReader::Values::mapped_type::value_type |
||||
mrw::ConfigFileReader::parseValue(const std::string& file, |
||||
std::string::size_type& pos) |
||||
const throw(std::exception) { |
||||
// find the begin of the variable name
|
||||
std::string::size_type startName(file.rfind('\n', pos)); |
||||
if (startName==std::string::npos) startName=0; |
||||
startName=file.find_first_not_of("\n\t ", startName); |
||||
if (startName==pos) |
||||
throw mrw::invalid_argument |
||||
("empty variable name in file '"+_filename+"'\n" |
||||
"environment is: "+file.substr(startName-10>0?startName-10:0, pos+10)); |
||||
// find the end of the variable name
|
||||
std::string::size_type endName(file.find_last_not_of("\n\t ", pos-1)+1); |
||||
// find the begin of the variable contents
|
||||
std::string::size_type startVal(file.find_first_not_of("\n\t ", ++pos)); |
||||
if (startVal==std::string::npos) |
||||
return std::make_pair // end of file, empty value
|
||||
(file.substr(startName, endName-startName), Value("", pos, pos)); |
||||
// find the end of the variable contents
|
||||
std::string::size_type realStart(startVal); // doesn't cut the first quote
|
||||
std::string::size_type endVal(file[startVal]=='"' ? // quoted with "
|
||||
file.find('"', ++startVal) : |
||||
file[startVal]=='\'' ? // quoted with '
|
||||
file.find('\'', ++startVal) : |
||||
file.find_first_of("[=#", startVal)); // normal
|
||||
if (endVal!=std::string::npos && file[endVal]=='=') |
||||
endVal=file.rfind('\n', endVal); // jump before following variable name
|
||||
endVal = file.find_last_not_of("\n\t ", // remove trailing white spaces
|
||||
endVal!=std::string::npos?endVal-1:endVal)+1; |
||||
std::string::size_type realEnd(endVal!=std::string::npos && // quotes?
|
||||
(file[realStart]=='"' && file[endVal]=='"' || |
||||
file[realStart]=='\'' && file[endVal]=='\'') |
||||
? endVal+1 : endVal); |
||||
if (endVal<=startVal) |
||||
return std::make_pair // empty value
|
||||
(file.substr(startName, endName-startName), Value("", pos, pos)); |
||||
return std::make_pair // normal case
|
||||
(file.substr(startName, endName-startName), |
||||
Value(file.substr(startVal, endVal-startVal), realStart, pos=realEnd)); |
||||
} |
||||
|
||||
std::string mrw::ConfigFileReader::parseComment(const std::string& file, |
||||
std::string::size_type& pos) |
||||
const throw(std::bad_exception) { |
||||
std::string::size_type start(pos); |
||||
pos = file.find('\n', pos); |
||||
return file.substr(start, pos-start); |
||||
} |
||||
|
||||
// GNU g++ prior to 3.4 does not implement covariant returns
|
||||
#if __GNUC__ == 3 && __GNUC_MINOR__ < 4 |
||||
mrw::ConfigFileReader& |
||||
#else |
||||
mrw::ConfigFileWriter& |
||||
#endif |
||||
mrw::ConfigFileWriter::reload() throw(std::exception) { |
||||
return parse(_file=readFile()); |
||||
} |
||||
|
||||
mrw::ConfigFileWriter& mrw::ConfigFileWriter::save() throw(std::exception) { |
||||
if (_filename=="") return *this; |
||||
// work in changes
|
||||
bool changed(false); |
||||
for (Values::iterator sec(_values.begin()); sec!=_values.end(); ++sec) |
||||
for (Values::mapped_type::iterator var(sec->second.begin()); |
||||
var!=sec->second.end(); ++var) |
||||
if (var->second.changed) { |
||||
var->second.changed = false; |
||||
changed = true; |
||||
// check if we need quoting
|
||||
std::string value(var->second.value); |
||||
if (value.find_first_of("[=#")!=std::string::npos) |
||||
if (value.find('"')==std::string::npos) |
||||
value = '"'+value+'"'; |
||||
else if (value.find('\'')==std::string::npos) |
||||
value = '\''+value+'\''; |
||||
else |
||||
throw mrw::invalid_argument("Configuration value is not quotable: " |
||||
+value); |
||||
// recalculate all positions
|
||||
if (var->second.start && |
||||
value.size() != var->second.end-var->second.start) { |
||||
int diff(value.size()-var->second.end+var->second.start); |
||||
for (Values::iterator sec2(_values.begin()); |
||||
sec2!=_values.end(); ++sec2) |
||||
for (Values::mapped_type::iterator var2(sec2->second.begin()); |
||||
var2!=sec2->second.end(); ++var2) |
||||
if (var2->second.start>var->second.end) { |
||||
var2->second.start += diff; |
||||
var2->second.end += diff; |
||||
} |
||||
for (Sections::iterator sec2(_sections.begin()); |
||||
sec2!=_sections.end(); ++sec2) |
||||
if (sec2->second>=var->second.end) sec2->second += diff; |
||||
} |
||||
// set the new value
|
||||
if (var->second.start) // simple change
|
||||
_file.replace(var->second.start, var->second.end-var->second.start, |
||||
value); |
||||
else if (_sections.find(sec->first)!=_sections.end()) // new value
|
||||
_file.insert(_sections[sec->first], "\n"+var->first+" = "+value |
||||
+(_file[_sections[sec->first]]=='\n'?"":"\n")); |
||||
else { // even a new section
|
||||
_file+="\n["+sec->first+"]\n"+var->first+" = "+value; |
||||
_sections[sec->first] = _file.size(); |
||||
} |
||||
} |
||||
if (!changed) return *this; |
||||
// save to file
|
||||
std::ofstream file(_filename.c_str()); |
||||
if (!(file<<_file)) |
||||
throw mrw::invalid_argument("Cannot write file '"+_filename+"'"); |
||||
return *this; |
||||
} |
@ -0,0 +1,362 @@ |
||||
/** @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 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" |
||||
|
||||
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 / 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 |
@ -0,0 +1,16 @@ |
||||
xxx=yyy # this is a comment |
||||
[Section] |
||||
|
||||
abc= |
||||
def=hallo welt |
||||
ghi= |
||||
jkl= mn |
||||
op qr |
||||
st |
||||
|
||||
[Other Section] |
||||
|
||||
1234="5678=90" |
||||
|
||||
here we are = some contents # comments |
||||
here= |
@ -0,0 +1,24 @@ |
||||
xxx=0 # this is a comment |
||||
[Section] |
||||
|
||||
abc=1 |
||||
def="Und=Tschuess" |
||||
ghi=3 |
||||
jkl= 4 |
||||
|
||||
|
||||
guguseli zwei = dadaa |
||||
guguseli drei = dadaa |
||||
guguseli = dadaa |
||||
[Other Section] |
||||
|
||||
1234=5 |
||||
|
||||
here we are = 6 # comments |
||||
here=7 |
||||
yes = . |
||||
no no no = . |
||||
no no = . |
||||
[New Section] |
||||
a first one = sgadd |
||||
one more = . |
@ -0,0 +1,3 @@ |
||||
#! /bin/bash |
||||
|
||||
test -z "`diff configfile2.ini configfile.ini.result`" && rm configfile2.ini |
@ -0,0 +1,66 @@ |
||||
/** @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 |
||||
|
||||
|
||||
*/ |
||||
|
||||
#include <mrw/configfile.hpp> |
||||
#include <mrw/file.hpp> |
||||
#include <cppunit/TestFixture.h> |
||||
#include <cppunit/ui/text/TestRunner.h> |
||||
#include <cppunit/extensions/HelperMacros.h> |
||||
#include <cppunit/extensions/TestFactoryRegistry.h> |
||||
|
||||
class ConfigFileTest: public CppUnit::TestFixture {
|
||||
public: |
||||
void CheckFile() { |
||||
mrw::File::copy("configfile.ini", "configfile2.ini"); |
||||
mrw::ConfigFileWriter config("configfile2.ini"); |
||||
CPPUNIT_ASSERT(config("", "xxx", ".")=="yyy"); |
||||
CPPUNIT_ASSERT(config("Section", "abc", ".")==""); |
||||
CPPUNIT_ASSERT(config("Section", "def", ".")=="hallo welt"); |
||||
CPPUNIT_ASSERT(config("Section", "ghi", ".")==""); |
||||
CPPUNIT_ASSERT(config("Section", "jkl", ".")=="mn\n op qr\n st"); |
||||
CPPUNIT_ASSERT(config("Other Section", "1234", ".")=="5678=90"); |
||||
CPPUNIT_ASSERT(config("Other Section", "here we are", ".") |
||||
=="some contents"); |
||||
CPPUNIT_ASSERT(config("Other Section", "here", ".")==""); |
||||
config("", "xxx", ".")="0"; |
||||
config("Section", "abc", ".")="1"; |
||||
CPPUNIT_ASSERT(config("New Section", "a first one", "sgadd")=="sgadd"); |
||||
config("Section", "def", ".")="Und=Tschuess"; |
||||
config("Section", "ghi", ".")="3"; |
||||
config("Section", "jkl", ".")="4"; |
||||
config("Other Section", "1234", ".")="5"; |
||||
config("Other Section", "here we are", ".")="6"; |
||||
config("Other Section", "here", ".")="7"; |
||||
CPPUNIT_ASSERT(config("Other Section", "no no", ".")=="."); |
||||
CPPUNIT_ASSERT(config("Other Section", "no no no", ".")=="."); |
||||
CPPUNIT_ASSERT(config("Other Section", "yes", ".")=="."); |
||||
CPPUNIT_ASSERT(config("Section", "guguseli", "dadaa")=="dadaa"); |
||||
CPPUNIT_ASSERT(config("Section", "guguseli zwei", "dadaa")=="dadaa"); |
||||
CPPUNIT_ASSERT(config("Section", "guguseli drei", "dadaa")=="dadaa"); |
||||
CPPUNIT_ASSERT(config("New Section", "one more", ".")=="."); |
||||
} |
||||
CPPUNIT_TEST_SUITE(ConfigFileTest); |
||||
CPPUNIT_TEST(CheckFile); |
||||
CPPUNIT_TEST_SUITE_END(); |
||||
}; |
||||
CPPUNIT_TEST_SUITE_REGISTRATION(ConfigFileTest); |
||||
|
||||
int main() { |
||||
CppUnit::TextUi::TestRunner runner; |
||||
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); |
||||
return runner.run() ? 0 : 1; |
||||
} |
@ -0,0 +1,47 @@ |
||||
/** @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 |
||||
|
||||
|
||||
1 2 3 4 5 6 7 8 |
||||
5678901234567890123456789012345678901234567890123456789012345678901234567890 |
||||
*/ |
||||
#ifndef __MRW_FILE_HPP__ |
||||
#define __MRW_FILE_HPP__ |
||||
|
||||
#include <mrw/exception.hpp> |
||||
#include <string> |
||||
#include <fstream> |
||||
|
||||
namespace mrw { |
||||
class File { |
||||
public: |
||||
static void copy(std::string from, std::string to) throw (std::exception) { |
||||
std::ifstream is(from.c_str()); |
||||
if (!is) |
||||
throw mrw::invalid_argument("Cannot read file: '"+from+"'"); |
||||
is.seekg(0, std::ios::end); |
||||
int size(is.tellg()); |
||||
std::string contents(size, 0); // make buffer long enough to store all
|
||||
is.seekg(0, std::ios::beg); |
||||
is.read(contents.begin().operator->(), size); // hack to get the buffer
|
||||
if (!is.good() && is.eof()) |
||||
throw mrw::invalid_argument("Cannot read file: '"+from+"'"); |
||||
std::ofstream os(to.c_str()); |
||||
os<<contents; |
||||
if (!os) throw mrw::invalid_argument("Cannot write file: '"+to+"'"); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
#endif |
Loading…
Reference in new issue