2 * Copyright (C) 2008 Patrick Ohly
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY, TITLE, NONINFRINGEMENT or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 #include "FileConfigNode.h"
20 #include "EvolutionSyncClient.h"
21 #include "SyncEvolutionUtil.h"
23 #include <boost/scoped_array.hpp>
29 /** @TODO: replace stdio.h with streams */
31 FileConfigNode::FileConfigNode(const string &path, const string &fileName) :
40 void FileConfigNode::read()
42 string filename = m_path + "/" + m_fileName;
44 FILE *file = fopen(filename.c_str(), "r");
49 while (fgets(buffer, sizeof(buffer), file)) {
50 char *eol = strchr(buffer, '\n');
54 m_lines.push_back(buffer);
62 void FileConfigNode::flush()
70 string filename = m_path + "/" + m_fileName;
71 string tmpFilename = m_path + "/.#" + m_fileName;
73 FILE *file = fopen(tmpFilename.c_str(), "w");
75 for (list<string>::const_iterator it = m_lines.begin();
78 fprintf(file, "%s\n", it->c_str());
81 bool failed = ferror(file);
86 rename(tmpFilename.c_str(), filename.c_str())) {
87 EvolutionSyncClient::throwError(tmpFilename + ": " + strerror(errno));
90 EvolutionSyncClient::throwError(tmpFilename + ": " + strerror(errno));
98 * get property and value from line, if any present
100 static bool getContent(const string &line,
107 while (start < line.size() &&
108 isspace(line[start])) {
113 if (start == line.size()) {
117 // Comment? Potentially keep reading, might be commented out assignment.
119 if (line[start] == '#') {
120 if (!fuzzyComments) {
126 // recognize # <word> = <value> as commented out (= default) value
129 while (start < line.size() &&
130 isspace(line[start])) {
137 while (end < line.size() &&
138 !isspace(line[end])) {
141 property = line.substr(start, end - start);
145 while (start < line.size() &&
146 isspace(line[start])) {
149 if (start == line.size() ||
150 line[start] != '=') {
151 // invalid syntax or we tried to read a comment as assignment
157 while (start < line.size() &&
158 isspace(line[start])) {
162 value = line.substr(start);
163 // remove trailing white space: usually it is
164 // added accidentally by users
165 size_t numspaces = 0;
166 while (numspaces < value.size() &&
167 isspace(value[value.size() - 1 - numspaces])) {
170 value.erase(value.size() - numspaces);
172 // @TODO: strip quotation marks around value?!
178 * check whether the line contains the property and if so, extract its value
180 static bool getValue(const string &line,
181 const string &property,
188 return getContent(line, curProp, value, isComment, fuzzyComments) &&
189 !strcasecmp(curProp.c_str(), property.c_str());
192 string FileConfigNode::readProperty(const string &property) const {
195 for (list<string>::const_iterator it = m_lines.begin();
198 const string &line = *it;
201 if (getValue(line, property, value, isComment, false)) {
210 void FileConfigNode::readProperties(map<string, string> &props) const {
211 map<string, string> res;
212 string value, property;
214 for (list<string>::const_iterator it = m_lines.begin();
217 const string &line = *it;
219 if (getContent(line, property, value, isComment, false)) {
220 // don't care about the result: only the first instance
221 // of the property counts, so it doesn't matter when
222 // inserting it again later fails
223 props.insert(pair<string, string>(property, value));
228 void FileConfigNode::removeProperty(const string &property)
232 list<string>::iterator it = m_lines.begin();
233 while (it != m_lines.end()) {
234 const string &line = *it;
236 if (getValue(line, property, value, isComment, false)) {
237 it = m_lines.erase(it);
245 void FileConfigNode::setProperty(const string &property,
246 const string &newvalue,
247 const string &comment,
248 const string *defValue) {
251 bool isDefault = false;
254 *defValue == newvalue) {
258 newstr += property + " = " + newvalue;
260 for (list<string>::iterator it = m_lines.begin();
263 const string &line = *it;
266 if (getValue(line, property, oldvalue, isComment, true)) {
267 if (newvalue != oldvalue ||
268 isComment && !isDefault) {
276 // add each line of the comment as separate line in .ini file
277 if (comment.size()) {
278 list<string> commentLines;
279 ConfigProperty::splitComment(comment, commentLines);
280 if (m_lines.size()) {
281 m_lines.push_back("");
283 for (list<string>::const_iterator it = commentLines.begin();
284 it != commentLines.end();
286 m_lines.push_back(string("# " + *it));
290 m_lines.push_back(newstr);