Save configuration using file.hpp

master
Marc Wäckerlin 20 years ago
parent 4e91c2fe11
commit baa21d8110
  1. 31
      mrw/configfile.cpp
  2. 209
      mrw/configfile.hpp
  3. 291
      mrw/file.hpp

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $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 Revision 1.1 2005/01/07 00:31:38 marc
initial version initial version
@ -20,6 +23,7 @@
#include <mrw/configfile.hpp> #include <mrw/configfile.hpp>
#include <mrw/exception.hpp> #include <mrw/exception.hpp>
#include <mrw/string.hpp> #include <mrw/string.hpp>
#include <mrw/file.hpp>
#include <fstream> #include <fstream>
mrw::ConfigFileReader::Value& mrw::ConfigFileReader::Value&
@ -38,26 +42,9 @@ mrw::ConfigFileReader& mrw::ConfigFileReader::load(const std::string& filename)
} }
mrw::ConfigFileReader& mrw::ConfigFileReader::reload() throw(std::exception) { mrw::ConfigFileReader& mrw::ConfigFileReader::reload() throw(std::exception) {
return parse(readFile()); return parse(mrw::File::read(_filename));
}
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) mrw::ConfigFileReader& mrw::ConfigFileReader::parse(const std::string& file)
throw(std::exception) { throw(std::exception) {
_values.erase(_values.begin(), _values.end()); _values.erase(_values.begin(), _values.end());
@ -142,7 +129,7 @@ mrw::ConfigFileReader&
mrw::ConfigFileWriter& mrw::ConfigFileWriter&
#endif #endif
mrw::ConfigFileWriter::reload() throw(std::exception) { 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) { 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(); _sections[sec->first] = _file.size();
} }
} }
if (!changed) return *this; if (changed) mrw::File::save(_filename, _file);
// save to file
std::ofstream file(_filename.c_str());
if (!(file<<_file))
throw mrw::invalid_argument("Cannot write file '"+_filename+"'");
return *this; return *this;
} }

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $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 Revision 1.1 2005/01/07 00:31:38 marc
initial version initial version
@ -24,6 +27,8 @@ namespace mrw {
/** @defgroup config Configuration File Handler /** @defgroup config Configuration File Handler
@pre #include <mrw/configfile.hpp>
Read configuration parameters from a file. Read configuration parameters from a file.
@section configSyntax Configuration File Syntax @section configSyntax Configuration File Syntax
@ -162,178 +167,268 @@ namespace mrw {
*/ */
//@{ //@{
/// 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 variables with values. This class does not store anything in the
file, even if variables have changed. file, even if variables have changed.
@see If you want %to save changes %to the file, use
@ref ConfigFileWriter. @see If you want %to save changes %to the file, use @ref ConfigFileWriter.
@see For details, see @ref configSyntax. */ @see For details, see @ref configSyntax.
*/
class ConfigFileReader { class ConfigFileReader {
//............................................................... typedefs
public: public:
//------------------------------------------------------------------ Value
/// A configuration file value. /// A configuration file value.
class Value { class Value {
//............................................................ methods
public: public:
/// Get the value string.
/** Get the value string. Trailing and leading white spaces have /** @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 been truncated. If the variable was quoted, quotes have been
removed. removed.
@return the variable's value as string */
@return the variable's value as string
*/
operator const std::string&() const throw() { operator const std::string&() const throw() {
return value; return value;
} }
/// Get the value string.
/** Alternative access to the variable's contents. Trailing and /** @brief Get the value string.
Alternative access to the variable's contents. Trailing and
leading white spaces have been truncated. If the variable leading white spaces have been truncated. If the variable
was quoted, quotes have been removed. was quoted, quotes have been removed.
@return the variable's value as string */
@return the variable's value as string
*/
const std::string& operator()() const throw() { const std::string& operator()() const throw() {
return value; return value;
} }
/// Assign a new value.
/** Assign a new value. The new value is lost if you use the /** @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 ConfigFileReader, but it is stored in the destructor, if you
use the ConfigFileWriter instead. */ use the ConfigFileWriter instead.
*/
Value& operator=(const std::string& newValue) throw() { Value& operator=(const std::string& newValue) throw() {
changed = true; changed = true;
value = newValue; value = newValue;
return *this; return *this;
} }
/// Compare the value to a string. /// Compare the value to a string.
bool operator==(const std::string& o) const throw() { bool operator==(const std::string& o) const throw() {
return o==value; return o==value;
} }
/// Compare a string to the value. /// Compare a string to the value.
friend bool operator==(const std::string& o, const Value& v) throw() { friend bool operator==(const std::string& o, const Value& v) throw() {
return v==o; return v==o;
} }
//............................................................ methods
protected: protected:
friend class ConfigFileReader; friend class ConfigFileReader;
friend class ConfigFileWriter; friend class ConfigFileWriter;
Value(const std::string& v,
std::string::size_type s, std::string::size_type e) throw(): Value(const std::string& v, std::string::size_type s,
value(v), changed(false), start(s), end(e) { std::string::size_type e) throw():
} value(v), changed(false), start(s), end(e) {}
Value(const std::string& v) throw(): Value(const std::string& v) throw():
value(v), changed(true), start(0), end(0) { value(v), changed(true), start(0), end(0) {
} }
//.......................................................... variables
protected:
std::string value; std::string value;
bool changed; bool changed;
std::string::size_type start; std::string::size_type start;
std::string::size_type end; std::string::size_type end;
//............................................................ methods
private: private:
Value(); // not implemented, must be initialized Value(); // not implemented, must be initialized
Value& operator=(const Value&); // not implemented, no assignment Value& operator=(const Value&); // not implemented, no assignment
}; };
/// Uninitialized construction. //------------------------------------------------------------------------
/** Uninitialized construction. Use this constructor, if you want
to call @ref load() later. */ //................................................................ methods
public:
/** @brief Uninitialized construction.
Uninitialized construction. Use this constructor, if you
want to call @ref load() later.
*/
ConfigFileReader() throw() {} ConfigFileReader() throw() {}
/// Parse a file at construction.
/** This should be the normal case: load and parse the file at /** @brief Parse a file at construction.
This should be the normal case: load and parse the file at
construction. construction.
@param filename the name of the file to read @param filename the name of the file to read
@throw mrw::invalid_argument @throw mrw::invalid_argument
- if the file does not exist - if the file does not exist
- if reading the file fails - if reading the file fails
- if there is an unterminated section name in the file - if there is an unterminated section name in the file
- if there is an empty variable name in the file */ - if there is an empty variable name in the file
*/
ConfigFileReader(const std::string& filename) throw(std::exception): ConfigFileReader(const std::string& filename) throw(std::exception):
_filename(filename) { _filename(filename) {
reload(); reload();
} }
/// Copy construct from other ConfigFileReader. /// Copy construct from other ConfigFileReader.
ConfigFileReader(const ConfigFileReader& o) throw(): ConfigFileReader(const ConfigFileReader& o) throw():
_filename(o._filename), _values(o._values) { _filename(o._filename), _values(o._values) {
} }
virtual ~ConfigFileReader() throw() {} virtual ~ConfigFileReader() throw() {}
/// Copy from other ConfigFileReader. /// Copy from other ConfigFileReader.
ConfigFileReader& operator=(const ConfigFileReader& o) { ConfigFileReader& operator=(const ConfigFileReader& o) {
_filename = o._filename; _filename = o._filename;
_values = o._values; _values = o._values;
return *this; return *this;
} }
/// Access a value read from the file.
/** Access a value read from the file. /** @brief Access a value read from the file.
Access a value read from the file.
@param section the section in the configuration file @param section the section in the configuration file
@param name the variable name @param name the variable name
@param default_ the default value that is set, if the variable is @param default_ the default value that is set, if the variable is
not configured not configured
@return the Value you are looking for */ @return the Value you are looking for
*/
Value& operator()(const std::string& section, const std::string& name, Value& operator()(const std::string& section, const std::string& name,
const std::string& default_) const std::string& default_)
throw(std::bad_exception); throw(std::bad_exception);
/// Load and parse a new file.
/** Load and parse a new file. /** @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 @throw mrw::invalid_argument
- if the file does not exist - if the file does not exist
- if reading the file fails - if reading the file fails
- if there is an unterminated section name in the file - if there is an unterminated section name in the file
- if there is an empty variable name in the file */ - if there is an empty variable name in the file
*/
ConfigFileReader& load(const std::string& filename) throw(std::exception); ConfigFileReader& load(const std::string& filename) throw(std::exception);
/// Reload the last file again.
/** Reload the last file again. /** @brief Reload the last file again.
Reload the last file again.
@throw mrw::invalid_argument @throw mrw::invalid_argument
- if the file does not exist - if the file does not exist
- if reading the file fails - if reading the file fails
- if there is an unterminated section name in the file - if there is an unterminated section name in the file
- if there is an empty variable name in the file */ - if there is an empty variable name in the file
*/
virtual ConfigFileReader& reload() throw(std::exception); virtual ConfigFileReader& reload() throw(std::exception);
//............................................................... typedefs
protected: protected:
typedef std::map< std::string, std::map<std::string, Value> > Values; typedef std::map< std::string, std::map<std::string, Value> > Values;
typedef std::map<std::string, std::string::size_type> Sections; typedef std::map<std::string, std::string::size_type> Sections;
std::string readFile() const throw(std::exception);
//.............................................................. variables
protected:
std::string _filename;
Values _values;
Sections _sections;
//................................................................ methods
protected:
ConfigFileReader& parse(const std::string& file) throw(std::exception); ConfigFileReader& parse(const std::string& file) throw(std::exception);
std::string parseSection(const std::string& file, std::string parseSection(const std::string& file,
std::string::size_type& pos) std::string::size_type& pos)
const throw(std::exception); const throw(std::exception);
Values::mapped_type::value_type parseValue(const std::string& file, Values::mapped_type::value_type parseValue(const std::string& file,
std::string::size_type& pos) std::string::size_type& pos)
const throw(std::exception); const throw(std::exception);
std::string parseComment(const std::string& file, std::string parseComment(const std::string& file,
std::string::size_type& pos) std::string::size_type& pos)
const throw(std::bad_exception); const throw(std::bad_exception);
std::string _filename;
Values _values;
Sections _sections;
}; };
//============================================================================
/** @brief Parse configuration file and offer read&nbsp;/ write /** @brief Parse configuration file and offer read&nbsp;/ write
access to the contents. access to the contents.
Parse a configuration file that consists of sections containing Parse a configuration file that consists of sections containing
variables with values. This class stores the file in the destructor variables with values. This class stores the file in the destructor
if variables have changed. if variables have changed.
@see If you want %to leave the file untouched, use
@ref ConfigFileReader. @see If you want %to leave the file untouched, use @ref ConfigFileReader.
@see For details, see @ref configSyntax. */ @see For details, see @ref configSyntax.
*/
class ConfigFileWriter: virtual public ConfigFileReader { class ConfigFileWriter: virtual public ConfigFileReader {
//................................................................ methods
public: public:
/// Uninitialized construction.
/** @copydoc ConfigFileReader() */ /** @brief Uninitialized construction.
@copydoc ConfigFileReader()
*/
ConfigFileWriter() throw() {} ConfigFileWriter() throw() {}
/// Parse a file at construction.
/** @copydoc ConfigFileReader(const std::string& filename) */ /** @brief Parse a file at construction.
@copydoc ConfigFileReader(const std::string&)
*/
ConfigFileWriter(const std::string& filename) throw(std::exception) { ConfigFileWriter(const std::string& filename) throw(std::exception) {
load(filename); load(filename);
} }
/// Copy construct from other ConfigFileWriter. /// Copy construct from other ConfigFileWriter.
ConfigFileWriter(const ConfigFileWriter& o) throw(): ConfigFileWriter(const ConfigFileWriter& o) throw():
ConfigFileReader(o) { ConfigFileReader(o) {
} }
/// The file is stored at destruction. /// The file is stored at destruction.
virtual ~ConfigFileWriter() throw() { virtual ~ConfigFileWriter() throw() {
save(); save();
} }
/// Copy from other ConfigFileWriter. /// Copy from other ConfigFileWriter.
ConfigFileWriter& operator=(const ConfigFileWriter& o) { ConfigFileWriter& operator=(const ConfigFileWriter& o) {
ConfigFileReader::operator=(o); ConfigFileReader::operator=(o);
return *this; return *this;
} }
// GNU g++ prior to 3.4 does not implement covariant returns // GNU g++ prior to 3.4 does not implement covariant returns
# if __GNUC__ == 3 && __GNUC_MINOR__ < 4 # if __GNUC__ == 3 && __GNUC_MINOR__ < 4
/// Reload the last file again. /** @brief Reload the last file again.
/** @copydoc mrw::ConfigFileReader::reload()
@copydoc mrw::ConfigFileReader::reload()
@bug Bug in GNU g++ compiler: @bug Bug in GNU g++ compiler:
return type should be <code>ConfigFileWriter&</code>, return type should be <code>ConfigFileWriter&</code>,
but the compiler sais:<br> but the compiler sais:<br>
@ -342,19 +437,29 @@ namespace mrw {
covariant returns</code><br> covariant returns</code><br>
The problem is fixed since GNU g++ 3.4, so the signature The problem is fixed since GNU g++ 3.4, so the signature
will be changed as soon as you upgrade to the new compiler will be changed as soon as you upgrade to the new compiler
version. */ version.
*/
virtual ConfigFileReader& reload() throw(std::exception); virtual ConfigFileReader& reload() throw(std::exception);
# else # else
/// Reload the last file again. /** @brief Reload the last file again.
/** @copydoc mrw::ConfigFileReader::reload() */
@copydoc mrw::ConfigFileReader::reload()
*/
virtual ConfigFileWriter& reload() throw(std::exception); virtual ConfigFileWriter& reload() throw(std::exception);
# endif # endif
/// Saves changes back to the file.
/** All changed parameters are stored in the configuration /** @brief Saves changes back to the file.
file. The rest of the file is left untouched. */
All changed parameters are stored in the configuration
file. The rest of the file is left untouched.
*/
ConfigFileWriter& save() throw(std::exception); ConfigFileWriter& save() throw(std::exception);
//.............................................................. variables
protected: protected:
std::string _file; std::string _file;
}; };
//@} //@}

@ -1,3 +1,4 @@
/** @file /** @file
$Id$ $Id$
@ -9,6 +10,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $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 Revision 1.1 2005/01/07 00:31:38 marc
initial version initial version
@ -22,26 +26,289 @@
#include <mrw/exception.hpp> #include <mrw/exception.hpp>
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <sys/types.h>
#include <dirent.h>
namespace mrw { 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 { class File {
//................................................................ methods
public: public:
static void copy(std::string from, std::string to) throw (std::exception) {
std::ifstream is(from.c_str()); /** @brief Copy a file.
if (!is)
throw mrw::invalid_argument("Cannot read file: '"+from+"'"); Copy a file to a new location.
is.seekg(0, std::ios::end);
int size(is.tellg()); First the source file is fully read into memory, then written
std::string contents(size, 0); // make buffer long enough to store all to the destination. If the source file cannot be fully read,
is.seekg(0, std::ios::beg); the destination is left untouched.
is.read(contents.begin().operator->(), size); // hack to get the buffer
if (!is.good() && is.eof()) @throw mrw::invalid_argument if read or write fails
throw mrw::invalid_argument("Cannot read file: '"+from+"'"); @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()); std::ofstream os(to.c_str());
os<<contents; os<<read(from);
if (!os) throw mrw::invalid_argument("Cannot write file: '"+to+"'"); 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 #endif

Loading…
Cancel
Save