/** @file $Id$ $Date$ $Author$ @copy © Marc Wäckerlin @license LGPL, see file COPYING $Log$ Revision 1.3 2005/11/29 12:39:42 marc make it compilable with gcc 4.0.2 and newer doxygen Revision 1.2 2005/01/28 07:49:32 marc Save configuration using file.hxx Revision 1.1 2005/01/07 00:31:38 marc initial version 1 2 3 4 5 6 7 8 5678901234567890123456789012345678901234567890123456789012345678901234567890 */ #include #include #include #include #include mrw::ConfigFileReader::Value& mrw::ConfigFileReader::operator()(const std::string& section, const std::string& name, const std::string& default_) { _values[section].insert(std::make_pair(name, Value(default_))); return _values[section].find(name)->second; } mrw::ConfigFileReader& mrw::ConfigFileReader::load(const std::string& filename) { _filename = filename; return reload(); } mrw::ConfigFileReader& mrw::ConfigFileReader::reload() { return parse(mrw::File::read(_filename)); } mrw::ConfigFileReader& mrw::ConfigFileReader::parse(const std::string& file) { _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 { 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 { // 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 { 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() { #if __GNUC__ == 3 && __GNUC_MINOR__ < 4 return parse(_file=mrw::File::read(_filename)); #else return dynamic_cast (parse(_file=mrw::File::read(_filename))); #endif } mrw::ConfigFileWriter& mrw::ConfigFileWriter::save() { 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) { std::string::size_type 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) mrw::File::save(_filename, _file); return *this; }