1//
2// PropertyFileConfiguration.cpp
3//
4// Library: Util
5// Package: Configuration
6// Module: PropertyFileConfiguration
7//
8// Copyright (c) 2004-2009, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Util/PropertyFileConfiguration.h"
16#include "Poco/Exception.h"
17#include "Poco/String.h"
18#include "Poco/Path.h"
19#include "Poco/FileStream.h"
20#include "Poco/LineEndingConverter.h"
21#include "Poco/Ascii.h"
22
23
24using Poco::trim;
25using Poco::Path;
26
27
28namespace Poco {
29namespace Util {
30
31
32PropertyFileConfiguration::PropertyFileConfiguration() :
33 _preserveComment(false)
34{
35}
36
37
38PropertyFileConfiguration::PropertyFileConfiguration(std::istream& istr, bool preserveComment) :
39 _preserveComment(preserveComment)
40{
41 load(istr, preserveComment);
42}
43
44
45PropertyFileConfiguration::PropertyFileConfiguration(const std::string& path, bool preserveComment) :
46 _preserveComment(preserveComment)
47{
48 load(path, preserveComment);
49}
50
51
52PropertyFileConfiguration::~PropertyFileConfiguration()
53{
54}
55
56
57void PropertyFileConfiguration::load(std::istream& istr, bool preserveComment)
58{
59 _preserveComment = preserveComment;
60 clear();
61 _fileContent.clear();
62 _keyFileContentItMap.clear();
63
64 while (!istr.eof()) parseLine(istr);
65}
66
67
68void PropertyFileConfiguration::load(const std::string& path, bool preserveComment)
69{
70 Poco::FileInputStream istr(path);
71 if (istr.good())
72 load(istr, preserveComment);
73 else
74 throw Poco::OpenFileException(path);
75}
76
77
78void PropertyFileConfiguration::save(std::ostream& ostr) const
79{
80 if (_preserveComment)
81 {
82 for (FileContent::const_iterator it = _fileContent.begin(); it != _fileContent.end(); ++it)
83 ostr << *it;
84 }
85 else
86 {
87 for (MapConfiguration::iterator it = begin(); it != end(); ++it)
88 ostr << composeOneLine(it->first, it->second);
89 }
90}
91
92
93void PropertyFileConfiguration::save(const std::string& path) const
94{
95 Poco::FileOutputStream ostr(path);
96 if (ostr.good())
97 {
98 Poco::OutputLineEndingConverter lec(ostr);
99 save(lec);
100 lec.flush();
101 ostr.flush();
102 if (!ostr.good()) throw Poco::WriteFileException(path);
103 }
104 else throw Poco::CreateFileException(path);
105}
106
107
108// If _preserveComment is true, not only save key-value into map
109// but also save the entire file into _fileContent.
110// Otherwise, only save key-value into map.
111void PropertyFileConfiguration::parseLine(std::istream& istr)
112{
113 skipSpace(istr);
114
115 if (!istr.eof())
116 {
117 if (isComment(istr.peek()))
118 {
119 if (_preserveComment) saveComment(istr);
120 else skipLine(istr);
121 }
122 else saveKeyValue(istr);
123 }
124}
125
126
127void PropertyFileConfiguration::skipSpace(std::istream& istr) const
128{
129 while (!istr.eof() && Poco::Ascii::isSpace(istr.peek())) istr.get();
130}
131
132
133int PropertyFileConfiguration::readChar(std::istream& istr)
134{
135 for (;;)
136 {
137 int c = istr.get();
138 if (c == '\\')
139 {
140 c = istr.get();
141 switch (c)
142 {
143 case 't':
144 return '\t';
145 case 'r':
146 return '\r';
147 case 'n':
148 return '\n';
149 case 'f':
150 return '\f';
151 case '\r':
152 if (istr.peek() == '\n')
153 istr.get();
154 continue;
155 case '\n':
156 continue;
157 default:
158 return c;
159 }
160 }
161 else if (c == '\n' || c == '\r')
162 return 0;
163 else
164 return c;
165 }
166}
167
168
169void PropertyFileConfiguration::setRaw(const std::string& key, const std::string& value)
170{
171 MapConfiguration::setRaw(key, value);
172 if (_preserveComment)
173 {
174 // Insert the key-value to the end of _fileContent and update _keyFileContentItMap.
175 if (_keyFileContentItMap.count(key) == 0)
176 {
177 FileContent::iterator fit = _fileContent.insert(_fileContent.end(), composeOneLine(key, value));
178 _keyFileContentItMap[key] = fit;
179 }
180 // Update the key-value in _fileContent.
181 else
182 {
183 FileContent::iterator fit = _keyFileContentItMap[key];
184 *fit = composeOneLine(key, value);
185 }
186 }
187}
188
189
190void PropertyFileConfiguration::removeRaw(const std::string& key)
191{
192 MapConfiguration::removeRaw(key);
193 // remove the key from _fileContent and _keyFileContentItMap.
194 if (_preserveComment)
195 {
196 _fileContent.erase(_keyFileContentItMap[key]);
197 _keyFileContentItMap.erase(key);
198 }
199}
200
201bool PropertyFileConfiguration::isComment(int c) const
202{
203 return c == '#' || c == '!';
204}
205
206
207void PropertyFileConfiguration::saveComment(std::istream& istr)
208{
209 std::string comment;
210
211 int c = istr.get();
212 while (!isNewLine(c))
213 {
214 comment += (char) c;
215 c = istr.get();
216 }
217 comment += (char) c;
218
219 _fileContent.push_back(comment);
220}
221
222
223void PropertyFileConfiguration::skipLine(std::istream& istr) const
224{
225 int c = istr.get();
226 while (!isNewLine(c)) c = istr.get();
227}
228
229
230void PropertyFileConfiguration::saveKeyValue(std::istream& istr)
231{
232 int c = istr.get();
233
234 std::string key;
235 while (!isNewLine(c) && !isKeyValueSeparator(c))
236 {
237 key += (char) c;
238 c = istr.get();
239 }
240
241 std::string value;
242 if (isKeyValueSeparator(c))
243 {
244 c = readChar(istr);
245 while (!istr.eof() && c)
246 {
247 value += (char) c;
248 c = readChar(istr);
249 }
250 }
251
252 setRaw(trim(key), trim(value));
253}
254
255
256bool PropertyFileConfiguration::isNewLine(int c) const
257{
258 return c == std::char_traits<char>::eof() || c == '\n' || c == '\r';
259}
260
261
262std::string PropertyFileConfiguration::composeOneLine(const std::string& key, const std::string& value) const
263{
264 std::string result = key + ": ";
265
266 for (std::string::const_iterator its = value.begin(); its != value.end(); ++its)
267 {
268 switch (*its)
269 {
270 case '\t':
271 result += "\\t";
272 break;
273 case '\r':
274 result += "\\r";
275 break;
276 case '\n':
277 result += "\\n";
278 break;
279 case '\f':
280 result += "\\f";
281 break;
282 case '\\':
283 result += "\\\\";
284 break;
285 default:
286 result += *its;
287 break;
288 }
289 }
290
291 return result += "\n";
292}
293
294
295bool PropertyFileConfiguration::isKeyValueSeparator(int c) const
296{
297 return c == '=' || c == ':';
298}
299
300
301} } // namespace Poco::Util
302