Save configuration using file.hpp

master
Marc Wäckerlin 20 years ago
parent 4e91c2fe11
commit baa21d8110
  1. 37
      mrw/configfile.cpp
  2. 467
      mrw/configfile.hpp
  3. 303
      mrw/file.hpp

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
Revision 1.2 2005/01/28 07:49:32 marc
Save configuration using file.hpp
Revision 1.1 2005/01/07 00:31:38 marc
initial version
@ -20,12 +23,13 @@
#include <mrw/configfile.hpp>
#include <mrw/exception.hpp>
#include <mrw/string.hpp>
#include <mrw/file.hpp>
#include <fstream>
mrw::ConfigFileReader::Value&
mrw::ConfigFileReader::operator()(const std::string& section,
const std::string& name,
const std::string& default_)
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;
@ -38,26 +42,9 @@ mrw::ConfigFileReader& mrw::ConfigFileReader::load(const std::string& filename)
}
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;
return parse(mrw::File::read(_filename));
}
#include <iostream>
using namespace std;
mrw::ConfigFileReader& mrw::ConfigFileReader::parse(const std::string& file)
throw(std::exception) {
_values.erase(_values.begin(), _values.end());
@ -142,7 +129,7 @@ mrw::ConfigFileReader&
mrw::ConfigFileWriter&
#endif
mrw::ConfigFileWriter::reload() throw(std::exception) {
return parse(_file=readFile());
return parse(_file=mrw::File::read(_filename));
}
mrw::ConfigFileWriter& mrw::ConfigFileWriter::save() throw(std::exception) {
@ -193,10 +180,6 @@ mrw::ConfigFileWriter& mrw::ConfigFileWriter::save() throw(std::exception) {
_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+"'");
if (changed) mrw::File::save(_filename, _file);
return *this;
}

@ -9,11 +9,14 @@
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
Revision 1.2 2005/01/28 07:49:32 marc
Save configuration using file.hpp
Revision 1.1 2005/01/07 00:31:38 marc
initial version
*/
*/
#ifndef __MRW_CONFIGFILE_HPP__
#define __MRW_CONFIGFILE_HPP__
@ -24,6 +27,8 @@ namespace mrw {
/** @defgroup config Configuration File Handler
@pre #include <mrw/configfile.hpp>
Read configuration parameters from a file.
@section configSyntax Configuration File Syntax
@ -141,12 +146,12 @@ namespace mrw {
[Global] # start of section "Global"
multi line text = this is a text
that is longer than one
line!
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 '
# either in " or in '
@endverbatim
@section configFileCode Code Sample
@ -159,202 +164,302 @@ namespace mrw {
} catch (...) { // file read or evaluation error
}
@endcode
*/
*/
//@{
/// Parse configuration file and access the contents.
/** Parse a configuration file that consists of sections containing
//============================================================================
/** @brief 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. */
@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 {
//............................................................... typedefs
public:
//------------------------------------------------------------------ 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:
/// 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 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();
}
/// 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;
/// Copy construct from other ConfigFileReader.
ConfigFileReader(const ConfigFileReader& o) throw():
_filename(o._filename), _values(o._values) {
}
/// 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;
virtual ~ConfigFileReader() throw() {}
/// Copy from other ConfigFileReader.
ConfigFileReader& operator=(const ConfigFileReader& o) {
_filename = o._filename;
_values = o._values;
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;
}
/** @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
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;
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);
};
//============================================================================
/** @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. */
@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;
//................................................................ 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;
};
//@}

@ -1,3 +1,4 @@
/** @file
$Id$
@ -9,39 +10,305 @@
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
Revision 1.2 2005/01/28 07:49:32 marc
Save configuration using file.hpp
Revision 1.1 2005/01/07 00:31:38 marc
initial version
1 2 3 4 5 6 7 8
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>
#include <sys/types.h>
#include <dirent.h>
namespace mrw {
/** @defgroup sysFile File and System Utilities
@brief By now, some utilies for file handling, i.e. a copy command.
Some file and system specific utilities are missing in C++ and
often there is even no good alternative in C libraries.
*/
//@{
//============================================================================
/** @brief File handling utilities.
Utilities for file access in C++.
*/
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+"'");
}
//................................................................ methods
public:
/** @brief Copy a file.
Copy a file to a new location.
First the source file is fully read into memory, then written
to the destination. If the source file cannot be fully read,
the destination is left untouched.
@throw mrw::invalid_argument if read or write fails
@param from the source file name
@param to the destination file name
@note If the destination file already exists, it will be overwritten.
*/
static void copy(const std::string& from,
const std::string& to) throw(std::exception) {
std::ofstream os(to.c_str());
os<<read(from);
if (!os) throw mrw::invalid_argument("Cannot write file: '"+to+"'");
}
/** @brief Read a file fully into memory.
Read a file fully into memory.
@throw mrw::invalid_argument if read fails
@param filename the file to read
@return the file's contents
*/
static std::string read(const std::string& filename)
throw(std::exception) {
std::string contents; // declare on top to allow compiler optimization
std::ifstream is(filename.c_str());
std::string::size_type sz(size(is, filename));
contents.resize(sz); // make buffer long enough to store all
is.read(contents.begin().operator->(), sz); // hack to get the buffer
if (!is.good() && is.eof())
throw mrw::invalid_argument("Cannot read file: '"+filename+"'");
return contents;
};
/** @brief Save a string to a file.
A string is stored in a file, the whole file is overwritten by
the contents of the string. This is the counterpart of
read(const std::string&).
@throw mrw::invalid_argument if write fails
@param filename the name of the output file
@param contents the text to be written to the file
*/
static void save(const std::string& filename, const std::string& contents)
throw(std::exception) {
std::ofstream file(filename.c_str());
if (!(file<<contents))
throw mrw::invalid_argument("Cannot write file '"+filename+"'");
}
/** @brief Get the size of a file
Get the size of a file.
@throw mrw::invalid_argument if read fails
@param filename the file name
@return the file's size
*/
static std::string::size_type size(const std::string& filename)
throw(std::exception) {
std::ifstream is(filename.c_str());
if (!is)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
is.seekg(0, std::ios::end);
if (!is)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
return is.tellg();
}
/** @brief Get the size of a file
Get the size of a file.
@throw mrw::invalid_argument if read fails
@param file the filestream
@param filename the name of the file (required for the exception)
@return the file's size
*/
static std::string::size_type size(std::ifstream& file,
const std::string& filename = "")
throw(std::exception) {
if (!file)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
file.seekg(0, std::ios::end);
std::string::size_type sz(file.tellg());
file.seekg(0, std::ios::beg);
if (!file)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
return sz;
}
};
//============================================================================
class Dir {
//............................................................... typedefs
public:
//------------------------------------------------------------------ Entry
class Entry {
//........................................................... typedefs
public:
/** @brief The type of the actual file.
The type of the actual file.
@note On some systems UNKNOWN is the only
value returned.
*/
enum FileType {
UNKNOWN = DT_UNKNOWN, ///< The type is unknown.
REGULAR = DT_REG, ///< A regular file.
DIR = DT_DIR, ///< A directory.
FIFO = DT_FIFO, ///< A named pipe, or FIFO.
SOCKET = DT_SOCK, ///< A local-domain socket.
CHAR = DT_CHR, ///< A character device.
BLOCK = DT_BLK ///< A block device.
};
//............................................................ methods
public:
Entry(): _new(true) {}
/// Get the name of the actual file.
operator const std::string&() throw(std::bad_exception) {
if (_new) { // only convert to string once
_new = false;
_name = _dirent.d.d_name;
}
return _name;
}
/// Get the name of the actual file.
const std::string& operator()() throw(std::bad_exception) {
return *this;
}
/// Get the type of the actual file.
operator FileType() const throw() {
return FileType(_dirent.d.d_type);
}
//.......................................................... variables
private:
friend class Dir;
union { // for portability reasons, see "info readdir_r"
dirent d;
char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
} _dirent;
bool _new;
std::string _name;
//............................................................ methods
private:
dirent& entry() throw() {
_new = true;
return _dirent.d;
}
};
//------------------------------------------------------------------------
//................................................................ methods
public:
/** @brief Open a directory for reading the file list.
Open a directory for reading the file list.
@throw mrw::invalid_argument if directory cannot be opened
@param dir name of the directory to open
@param ignoreDots ignore directories <code>.<code> and
<code>..</code> (self anf top)
*/
Dir(const std::string& dir, bool ignoreDots=true) throw(std::exception):
_ignoreDots(ignoreDots), _dir(opendir(dir.c_str())) {
if (!_dir)
throw mrw::invalid_argument("Cannot read directory: '"+dir+'\'');
}
/// Directory is closed automatically in the destructor.
~Dir() throw() {
closedir(_dir);
}
/** @brief Advance to the next directory entry.
Advance to the next directory entry.
@code
mrw::Dir dir("/home");
while (dir) ...
@endcode
@return
- @c true if an entry has been found
- @c false if there are no more entries left
@warning If you call this method again after @c false was
returned, behaviour is unspecified, but a crash
is probable.
*/
operator bool() throw() {
static const std::string D("."), DD("..");
static dirent* fake;
if (readdir_r(_dir, &_entry.entry(), &fake)) return false;
if (_ignoreDots && _entry==Entry::DIR && (D==_entry() || D==_entry()))
return operator bool();
return true;
}
/** @brief Get the actual directory entry.
Returns the actual directory entry.
@code
mrw::Dir dir("/home");
while (dir) {
dirent entry(dir());
...
}
@endcode
@return the actual directory entry
@warning You must first call operator bool(), otherwise the
behaviour is undefined, but a crash is probable.
*/
const Entry& operator()() {
return _entry;
}
//.............................................................. variables
private:
bool _ignoreDots;
DIR* _dir;
Entry _entry;
};
//@}
}
#endif

Loading…
Cancel
Save