From b69f0daa1aab62ad2255906c8b3dbad742afb9c7 Mon Sep 17 00:00:00 2001 From: James Turk Date: Mon, 31 Jan 2005 15:44:13 +0000 Subject: [PATCH] ConfigFile rewrite --- devcpp/photon.dev | 6 +- include/util/ConfigFile.h | 273 ++++++++++++++++++++++++++++++++++---- src/util/ConfigFile.cpp | 175 +++++++++++++++++++++++- 3 files changed, 421 insertions(+), 33 deletions(-) diff --git a/devcpp/photon.dev b/devcpp/photon.dev index b555b71..b7400ba 100644 --- a/devcpp/photon.dev +++ b/devcpp/photon.dev @@ -17,8 +17,8 @@ IsCpp=1 Icon= ExeOutput=..\lib ObjectOutput=..\devcpp -OverrideOutput=0 -OverrideOutputName=photon.a +OverrideOutput=1 +OverrideOutputName=libphoton.a HostApplication= Folders=include,include/util,src,src/util CommandLine= @@ -27,7 +27,7 @@ CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 -CompilerSettings=0000000000000000000000 +CompilerSettings=0000001000000000000000 [Unit1] FileName=..\include\types.h diff --git a/include/util/ConfigFile.h b/include/util/ConfigFile.h index 84a2752..c750bee 100644 --- a/include/util/ConfigFile.h +++ b/include/util/ConfigFile.h @@ -1,28 +1,247 @@ -//This file is part of Photon (http://photon.sourceforge.net) -//Copyright (C) 2004-2005 James Turk -// -// Author: -// James Turk (jpt2433@rit.edu) -// -// Version: -// $Id: ConfigFile.h,v 1.1 2005/01/27 03:35:23 cozman Exp $ -// -// Revisions: +//This file is part of Photon (http://photon.sourceforge.net) +//Copyright (C) 2004-2005 James Turk +// +// Author: +// James Turk (jpt2433@rit.edu) +// +// Version: +// $Id: ConfigFile.h,v 1.2 2005/01/31 15:44:13 cozman Exp $ +// +// Revisions: // $Log: ConfigFile.h,v $ -// Revision 1.1 2005/01/27 03:35:23 cozman -// initial import (exceptions,types, and logging,oh my!) -// -// - -#ifndef PHOTON_UTIL_CONFIGFILE_H -#define PHOTON_UTIL_CONFIGFILE_H - -namespace photon { -namespace util { - - - -} -} - -#endif //PHOTON_UTIL_CONFIGFILE_H +// Revision 1.2 2005/01/31 15:44:13 cozman +// ConfigFile rewrite +// +// Revision 1.1 2005/01/27 03:35:23 cozman +// initial import (exceptions,types, and logging,oh my!) +// +// + +#ifndef PHOTON_UTIL_CONFIGFILE_H +#define PHOTON_UTIL_CONFIGFILE_H + +#include +#include +#include +#include +#include + +namespace photon +{ +namespace util +{ + +// Class: ConfigFile +// ConfigFile class, for reading/writing INI-style config files. +// +// File format is fairly flexible, whitespace and comments beginning with # or +// ; are ignored & left intact. +// +// Use []'s to denote sections. +// +// Variables are defined in var=val format. +class ConfigFile +{ + +public: // types used in ConfigFile + + //predicate for search + template + class StrPairEq + { + public: + typedef pairT first_argument_type; + typedef std::string second_argument_type; + typedef bool result_type; + bool operator()(const pairT& lhs, const std::string& rhs) const; + }; + + typedef std::pair Variable; + typedef std::list< Variable > Section; + typedef std::pair< std::string, std::list< Variable > > NamedSection; + typedef std::list< NamedSection > Layout; + +// Group: (Con/De)structors +public: + + // Function: ConfigFile + // Default constructor for ConfigFile. + ConfigFile(); + + // Function: ConfigFile + // Constructor for ConfigFile, calls . + // + // Parameters: + // filename - Name of ConfigFile to open. + ConfigFile(const std::string& filename); + + // Function: ~ConfigFile + // Calls close upon the ConfigFile. + virtual ~ConfigFile(); + +// Group: File Access +public: + + // Function: open + // open a file, processing it as an INI-like config file. + // + // Parameters: + // filename - Name of ConfigFile to open. + void open(const std::string& filename); + + // Function: flush + // Flushes the data written to the config file to disk, generally needs + // not be called. + void flush(); + + // Function: close + // Flushes the data and closes, open must be called again before using same + // ConfigFile. + void close(); + + // Function: setVariable + // Template function for setting variables in the config file. + // + // WARNING: Do not try to use this with user-defined types, numeric types + // and strings work fine, and this is all that should be contained in + // an INI. + // + // Parameters: + // sec - section name within config file + // var - variable name within section + // value - value to set variable equal to + template + void setVariable(const std::string& sec, + const std::string& var, + varType value); + + // Function: getVariable + // Template function for getting values from the config file. Supports + // returning a default value if the desired variable was not found. + // + // WARNING: Do not try to use this with user-defined types, numeric types + // and strings work fine, and this is all that should be contained in + // an INI. + // + // Parameters: + // sec - section name within config file + // var - variable name within section + // defVal - value to return if variable does not exist + // + // Returns: + // Value of variable within config file or defVal if value was not found. + template + varType getVariable(const std::string& sec, + const std::string& var, + varType defVal) const; + +private: + static std::string cleanString(const std::string& str); + static std::string bracketString(const std::string& str); + +private: + Layout layout_; + std::string filename_; + +}; + +//predicate for search +template +bool ConfigFile::StrPairEq::operator()(const pairT& lhs, + const std::string& rhs) const +{ + return ConfigFile::cleanString(lhs.first) == ConfigFile::cleanString(rhs); +} + +//Template implementation +template +void +ConfigFile::setVariable(const std::string& sec, + const std::string& var, + varType value) +{ + std::string secBrac(bracketString(sec)); + Layout::iterator secIter; + Section::iterator varIter; + std::ostringstream ss; + + ss << value; //write value to string + + //search for section + secIter = std::find_if( layout_.begin(), + layout_.end(), + std::bind2nd(StrPairEq(), secBrac) ); + + // add the section if it does not exist + if(secIter == layout_.end()) + { + layout_.push_back( NamedSection( secBrac, Section() ) ); + + //search again, assert that it now exists + secIter = std::find_if( layout_.begin(), + layout_.end(), + std::bind2nd(StrPairEq(), secBrac) ); + assert(secIter != layout_.end()); + } + + //search for variable + varIter = std::find_if( secIter->second.begin(), + secIter->second.end(), + std::bind2nd(StrPairEq(), var) ); + + // add the variable if it does not exist + if(varIter == secIter->second.end()) + { + secIter->second.push_back( Variable(var, ss.str()) ); + } + else + { + varIter->second = ss.str(); + } +} + +//template specialization for setVariable +/*template<> +void +ConfigFile::setVariable(std::string sec, std::string var, std::string value) +{ + sec = "[" + sec + "]"; //add []s to section name + value = "\"" + value + "\""; //add ""s to value + + layout_[sec][var] = value; //actually set it +}*/ + +template +varType +ConfigFile::getVariable(const std::string& sec, + const std::string& var, + varType defVal) const +{ + std::string secBrac(bracketString(sec)); + std::stringstream ss; + varType ret(defVal); + Layout::const_iterator secIter; + Section::const_iterator varIter; + + secIter = std::find_if( layout_.begin(), + layout_.end(), + std::bind2nd(StrPairEq(), secBrac) ); + if(secIter != layout_.end()) + { + varIter = std::find_if( secIter->second.begin(), + secIter->second.end(), + std::bind2nd(StrPairEq(), var) ); + if(varIter != secIter->second.end()) + { + ss.str(varIter->second); + ss >> ret; + } + } + + return ret; +} + +} +} + +#endif //PHOTON_UTIL_CONFIGFILE_H diff --git a/src/util/ConfigFile.cpp b/src/util/ConfigFile.cpp index c35536a..49df1f4 100644 --- a/src/util/ConfigFile.cpp +++ b/src/util/ConfigFile.cpp @@ -5,22 +5,191 @@ // James Turk (jpt2433@rit.edu) // // Version: -// $Id: ConfigFile.cpp,v 1.1 2005/01/27 03:35:24 cozman Exp $ +// $Id: ConfigFile.cpp,v 1.2 2005/01/31 15:44:13 cozman Exp $ // // Revisions: // $Log: ConfigFile.cpp,v $ +// Revision 1.2 2005/01/31 15:44:13 cozman +// ConfigFile rewrite +// // Revision 1.1 2005/01/27 03:35:24 cozman // initial import (exceptions,types, and logging,oh my!) // // #include "util/ConfigFile.h" +#include "exceptions.h" +#include +#include + +namespace photon +{ +namespace util +{ -namespace photon { -namespace util { +//(Con/De)structors +ConfigFile::ConfigFile() +{ +} + +ConfigFile::ConfigFile(const std::string& filename) +{ + open(filename); +} + +ConfigFile::~ConfigFile() +{ + close(); +} + +//File Access + +void ConfigFile::open(const std::string& filename) +{ + filename_ = filename; + if(filename_.empty()) + { + throw PreconditionException("No filename in ConfigFile::open"); + } + + std::string section, var, val, str, clean; + std::ifstream file(filename_.c_str()); + + if(!file) + { + throw Error("Unable to open " + filename_ + + " for reading in ConfigFile::open"); + } + + layout_.clear(); //clear layout, just in case + + while(file) //parses entire file + { + std::getline(file,str); //read in a line + clean = cleanString(str); //get a clean version + + //if std::string is bracketed it is a section + if(clean[0] == '[' && clean[clean.length()-1] == ']') + { + section = str; + } + else if(std::isalpha(clean[0])) //variables must start with a letter + { + //split at the equals sign + var = str.substr(0,str.find('=')); + val = str.substr(str.find('=')+1); + setVariable(section,var,val); + } + else if(clean[0] == '#' || clean[0] == ';') //comments + { + setVariable(section,"_comment",str); + } + else if(clean.length() == 0 && !file.eof()) //save blank lines + { + setVariable(section,"_newline",str); + } + + } + file.close(); +} + +void ConfigFile::flush() +{ + Layout::iterator sec; + Section::iterator var; + std::string secName; + + //in case the filename is already cleared + if(filename_.empty()) + { + throw PreconditionException("No filename in ConfigFile::flush"); + } + + //open the file blanked out, to not duplicate entries + std::ofstream file(filename_.c_str(), std::ios::out|std::ios::trunc); + + if(!file) + { + throw Error("Unable to open " + filename_ + + " for writing in ConfigFile::flush"); + } + + //iteration through sections + for(sec = layout_.begin(); sec != layout_.end(); ++sec) + { + //ensure that section is valid + secName = cleanString(sec->first); + if(secName[0] == '[' && secName[secName.length()-1] == ']') + { + file << sec->first << std::endl; //write out raw section title + + //for each variable in section, write out variable=value + for(var = sec->second.begin(); var != sec->second.end(); ++var) + { + if(var->first[0] == '_') //special values start with _ + { + if(var->first.substr(1) == "comment") + { + file << var->second << std::endl; + } + else if(var->first.substr(1) == "newline") + { + file << std::endl; + } + } + else if(!cleanString(var->first).empty()) //a variable + { + file << var->first << '=' << var->second << std::endl; + } + } + } + } + file.close(); +} + +void ConfigFile::close() +{ + //flush and clear out the data members + flush(); + filename_ = std::string(); + layout_.clear(); +} + +std::string ConfigFile::cleanString(const std::string& str) +{ + std::string ret(str); + + //convert to lower case + std::transform( ret.begin(), ret.end(), ret.begin(), + static_cast(std::tolower) ); + + //remove all spaces + for(std::string::iterator i=ret.begin(); i != ret.end(); ++i) + { + if(std::isspace(*i)) + { + i = ret.erase(i); + } + } + + return ret; +} + +std::string ConfigFile::bracketString(const std::string& str) +{ + std::string ret( cleanString(str) ); + + //add brackets if they do not exist, unless the string is empty + if(!ret.empty() && ret[0] != '[' || ret[ret.length()-1] != ']') + { + ret = "["+ret+"]"; + } + + return ret; +} } }