00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "FileConfigNode.h"
00021 #include "EvolutionSyncClient.h"
00022 #include "SyncEvolutionUtil.h"
00023
00024 #include <boost/scoped_array.hpp>
00025 #include <boost/foreach.hpp>
00026
00027 #include <unistd.h>
00028 #include <errno.h>
00029 #include <fcntl.h>
00030
00031
00032
00033 FileConfigNode::FileConfigNode(const string &path, const string &fileName) :
00034 m_path(path),
00035 m_fileName(fileName),
00036 m_modified(false),
00037 m_exists(false)
00038 {
00039 read();
00040 }
00041
00042 void FileConfigNode::read()
00043 {
00044 string filename = m_path + "/" + m_fileName;
00045
00046 FILE *file = fopen(filename.c_str(), "r");
00047 char buffer[512];
00048
00049 m_lines.clear();
00050 if (file) {
00051 while (fgets(buffer, sizeof(buffer), file)) {
00052 char *eol = strchr(buffer, '\n');
00053 if (eol) {
00054 *eol = 0;
00055 }
00056 m_lines.push_back(buffer);
00057 }
00058 m_exists = true;
00059 fclose(file);
00060 }
00061 m_modified = false;
00062 }
00063
00064 void FileConfigNode::flush()
00065 {
00066 if (!m_modified) {
00067 return;
00068 }
00069
00070 mkdir_p(m_path);
00071
00072 string filename = m_path + "/" + m_fileName;
00073 string tmpFilename = m_path + "/.#" + m_fileName;
00074
00075 FILE *file = fopen(tmpFilename.c_str(), "w");
00076 if (file) {
00077 BOOST_FOREACH(const string &line, m_lines) {
00078 fprintf(file, "%s\n", line.c_str());
00079 }
00080 fflush(file);
00081 bool failed = ferror(file);
00082 if (fclose(file)) {
00083 failed = true;
00084 }
00085 if (failed ||
00086 rename(tmpFilename.c_str(), filename.c_str())) {
00087 EvolutionSyncClient::throwError(tmpFilename, errno);
00088 }
00089 } else {
00090 EvolutionSyncClient::throwError(tmpFilename, errno);
00091 }
00092
00093 m_modified = false;
00094 m_exists = true;
00095 }
00096
00097
00098
00099
00100 static bool getContent(const string &line,
00101 string &property,
00102 string &value,
00103 bool &isComment,
00104 bool fuzzyComments)
00105 {
00106 size_t start = 0;
00107 while (start < line.size() &&
00108 isspace(line[start])) {
00109 start++;
00110 }
00111
00112
00113 if (start == line.size()) {
00114 return false;
00115 }
00116
00117
00118 isComment = false;
00119 if (line[start] == '#') {
00120 if (!fuzzyComments) {
00121 return false;
00122 }
00123 isComment = true;
00124 }
00125
00126
00127 if (isComment) {
00128 start++;
00129 while (start < line.size() &&
00130 isspace(line[start])) {
00131 start++;
00132 }
00133 }
00134
00135
00136 size_t end = start;
00137 while (end < line.size() &&
00138 !isspace(line[end])) {
00139 end++;
00140 }
00141 property = line.substr(start, end - start);
00142
00143
00144 start = end;
00145 while (start < line.size() &&
00146 isspace(line[start])) {
00147 start++;
00148 }
00149 if (start == line.size() ||
00150 line[start] != '=') {
00151
00152 return false;
00153 }
00154
00155
00156 start++;
00157 while (start < line.size() &&
00158 isspace(line[start])) {
00159 start++;
00160 }
00161
00162 value = line.substr(start);
00163
00164
00165 size_t numspaces = 0;
00166 while (numspaces < value.size() &&
00167 isspace(value[value.size() - 1 - numspaces])) {
00168 numspaces++;
00169 }
00170 value.erase(value.size() - numspaces);
00171
00172
00173
00174 return true;
00175 }
00176
00177
00178
00179
00180 static bool getValue(const string &line,
00181 const string &property,
00182 string &value,
00183 bool &isComment,
00184 bool fuzzyComments)
00185
00186 {
00187 string curProp;
00188 return getContent(line, curProp, value, isComment, fuzzyComments) &&
00189 !strcasecmp(curProp.c_str(), property.c_str());
00190 }
00191
00192 string FileConfigNode::readProperty(const string &property) const {
00193 string value;
00194
00195 BOOST_FOREACH(const string &line, m_lines) {
00196 bool isComment;
00197
00198 if (getValue(line, property, value, isComment, false)) {
00199 return value;
00200 }
00201 }
00202 return "";
00203 }
00204
00205
00206
00207 void FileConfigNode::readProperties(map<string, string> &props) const {
00208 map<string, string> res;
00209 string value, property;
00210
00211 BOOST_FOREACH(const string &line, m_lines) {
00212 bool isComment;
00213 if (getContent(line, property, value, isComment, false)) {
00214
00215
00216
00217 props.insert(pair<string, string>(property, value));
00218 }
00219 }
00220 }
00221
00222 void FileConfigNode::removeProperty(const string &property)
00223 {
00224 string value;
00225
00226 list<string>::iterator it = m_lines.begin();
00227 while (it != m_lines.end()) {
00228 const string &line = *it;
00229 bool isComment;
00230 if (getValue(line, property, value, isComment, false)) {
00231 it = m_lines.erase(it);
00232 m_modified = true;
00233 } else {
00234 it++;
00235 }
00236 }
00237 }
00238
00239 void FileConfigNode::setProperty(const string &property,
00240 const string &newvalue,
00241 const string &comment,
00242 const string *defValue) {
00243 string newstr;
00244 string oldvalue;
00245 bool isDefault = false;
00246
00247 if (defValue &&
00248 *defValue == newvalue) {
00249 newstr += "# ";
00250 isDefault = true;
00251 }
00252 newstr += property + " = " + newvalue;
00253
00254 BOOST_FOREACH(string &line, m_lines) {
00255 bool isComment;
00256
00257 if (getValue(line, property, oldvalue, isComment, true)) {
00258 if (newvalue != oldvalue ||
00259 isComment && !isDefault) {
00260 line = newstr;
00261 m_modified = true;
00262 }
00263 return;
00264 }
00265 }
00266
00267
00268 if (comment.size()) {
00269 list<string> commentLines;
00270 ConfigProperty::splitComment(comment, commentLines);
00271 if (m_lines.size()) {
00272 m_lines.push_back("");
00273 }
00274 BOOST_FOREACH(const string &comment, commentLines) {
00275 m_lines.push_back(string("# ") + comment);
00276 }
00277 }
00278
00279 m_lines.push_back(newstr);
00280 m_modified = true;
00281 }
00282