2 * Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
3 * Copyright (C) 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) version 3.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #include <syncevo/IniConfigNode.h>
22 #include <syncevo/FileDataBlob.h>
23 #include <syncevo/SyncConfig.h>
24 #include <syncevo/util.h>
26 #include <boost/scoped_array.hpp>
27 #include <boost/foreach.hpp>
29 #include <syncevo/declarations.h>
33 IniBaseConfigNode::IniBaseConfigNode(const boost::shared_ptr<DataBlob> &data) :
38 void IniBaseConfigNode::flush()
44 if (m_data->isReadonly()) {
45 throw std::runtime_error(m_data->getName() + ": internal error: flushing read-only config node not allowed");
48 boost::shared_ptr<std::ostream> file = m_data->write();
54 IniFileConfigNode::IniFileConfigNode(const boost::shared_ptr<DataBlob> &data) :
55 IniBaseConfigNode(data)
60 IniFileConfigNode::IniFileConfigNode(const std::string &path, const std::string &fileName, bool readonly) :
61 IniBaseConfigNode(boost::shared_ptr<DataBlob>(new FileDataBlob(path, fileName, readonly)))
68 void IniFileConfigNode::toFile(std::ostream &file) {
69 BOOST_FOREACH(const string &line, m_lines) {
70 file << line << std::endl;
74 void IniFileConfigNode::read()
76 boost::shared_ptr<std::istream> file(m_data->read());
78 while (getline(*file, line)) {
79 m_lines.push_back(line);
86 * get property and value from line, if any present
88 static bool getContent(const string &line,
95 while (start < line.size() &&
96 isspace(line[start])) {
101 if (start == line.size()) {
105 // Comment? Potentially keep reading, might be commented out assignment.
107 if (line[start] == '#') {
108 if (!fuzzyComments) {
114 // recognize # <word> = <value> as commented out (= default) value
117 while (start < line.size() &&
118 isspace(line[start])) {
125 while (end < line.size() &&
126 !isspace(line[end])) {
129 property = line.substr(start, end - start);
133 while (start < line.size() &&
134 isspace(line[start])) {
137 if (start == line.size() ||
138 line[start] != '=') {
139 // invalid syntax or we tried to read a comment as assignment
145 while (start < line.size() &&
146 isspace(line[start])) {
150 value = line.substr(start);
151 // remove trailing white space: usually it is
152 // added accidentally by users
153 size_t numspaces = 0;
154 while (numspaces < value.size() &&
155 isspace(value[value.size() - 1 - numspaces])) {
158 value.erase(value.size() - numspaces);
160 // @TODO: strip quotation marks around value?!
166 * check whether the line contains the property and if so, extract its value
168 static bool getValue(const string &line,
169 const string &property,
176 return getContent(line, curProp, value, isComment, fuzzyComments) &&
177 !strcasecmp(curProp.c_str(), property.c_str());
180 InitStateString IniFileConfigNode::readProperty(const string &property) const
184 BOOST_FOREACH(const string &line, m_lines) {
187 if (getValue(line, property, value, isComment, false)) {
188 return InitStateString(value, true);
191 return InitStateString();
194 void IniFileConfigNode::readProperties(ConfigProps &props) const {
195 map<string, string> res;
196 string value, property;
198 BOOST_FOREACH(const string &line, m_lines) {
200 if (getContent(line, property, value, isComment, false)) {
201 // don't care about the result: only the first instance
202 // of the property counts, so it doesn't matter when
203 // inserting it again later fails
204 props.insert(ConfigProps::value_type(property, InitStateString(value, true)));
209 void IniFileConfigNode::removeProperty(const string &property)
213 list<string>::iterator it = m_lines.begin();
214 while (it != m_lines.end()) {
215 const string &line = *it;
217 if (getValue(line, property, value, isComment, false)) {
218 it = m_lines.erase(it);
226 void IniFileConfigNode::writeProperty(const string &property,
227 const InitStateString &newvalue,
228 const string &comment) {
231 bool isDefault = false;
233 if (!newvalue.wasSet()) {
237 newstr += property + " = " + newvalue;
239 BOOST_FOREACH(string &line, m_lines) {
242 if (getValue(line, property, oldvalue, isComment, true)) {
243 if (newvalue != oldvalue ||
244 (isComment && !isDefault)) {
252 // add each line of the comment as separate line in .ini file
253 if (comment.size()) {
254 list<string> commentLines;
255 ConfigProperty::splitComment(comment, commentLines);
256 if (m_lines.size()) {
257 m_lines.push_back("");
259 BOOST_FOREACH(const string &comment, commentLines) {
260 m_lines.push_back(string("# ") + comment);
264 m_lines.push_back(newstr);
268 void IniFileConfigNode::clear()
274 IniHashConfigNode::IniHashConfigNode(const boost::shared_ptr<DataBlob> &data) :
275 IniBaseConfigNode(data)
280 IniHashConfigNode::IniHashConfigNode(const string &path, const string &fileName, bool readonly) :
281 IniBaseConfigNode(boost::shared_ptr<DataBlob>(new FileDataBlob(path, fileName, readonly)))
286 void IniHashConfigNode::read()
288 boost::shared_ptr<std::istream> file(m_data->read());
290 while (std::getline(*file, line)) {
291 string property, value;
293 if (getContent(line, property, value, isComment, false)) {
294 m_props.insert(StringPair(property, value));
300 void IniHashConfigNode::toFile(std::ostream &file)
302 BOOST_FOREACH(const StringPair &prop, m_props) {
303 file << prop.first << " = " << prop.second << std::endl;
307 void IniHashConfigNode::readProperties(ConfigProps &props) const
309 BOOST_FOREACH(const StringPair &prop, m_props) {
310 props.insert(ConfigProps::value_type(prop.first, InitStateString(prop.second, true)));
314 void IniHashConfigNode::writeProperties(const ConfigProps &props)
316 if (!props.empty()) {
317 m_props.insert(props.begin(), props.end());
323 InitStateString IniHashConfigNode::readProperty(const string &property) const
325 std::map<std::string, std::string>::const_iterator it = m_props.find(property);
326 if (it != m_props.end()) {
327 return InitStateString(it->second, true);
329 return InitStateString();
333 void IniHashConfigNode::removeProperty(const string &property) {
334 map<string, string>::iterator it = m_props.find(property);
335 if(it != m_props.end()) {
341 void IniHashConfigNode::clear()
343 if (!m_props.empty()) {
349 void IniHashConfigNode::writeProperty(const string &property,
350 const InitStateString &newvalue,
351 const string &comment)
353 // we only store explicitly set properties
354 if (!newvalue.wasSet()) {
355 removeProperty(property);
358 map<string, string>::iterator it = m_props.find(property);
359 if(it != m_props.end()) {
360 if (it->second != newvalue) {
361 it->second = newvalue;
365 m_props.insert(StringPair(property, newvalue));