|
a/src/utils/conftree.h |
|
b/src/utils/conftree.h |
|
... |
|
... |
21 |
* A simple configuration file implementation.
|
21 |
* A simple configuration file implementation.
|
22 |
*
|
22 |
*
|
23 |
* Configuration files have lines like 'name = value', and/or like '[subkey]'
|
23 |
* Configuration files have lines like 'name = value', and/or like '[subkey]'
|
24 |
*
|
24 |
*
|
25 |
* Lines like '[subkey]' in the file define subsections, with independant
|
25 |
* Lines like '[subkey]' in the file define subsections, with independant
|
26 |
* configuration namespaces. Only subsections holding at least one variable are
|
26 |
* configuration namespaces. Only subsections holding at least one variable are
|
27 |
* significant (empty subsections may be deleted during an update, or not)
|
27 |
* significant (empty subsections may be deleted during an update, or not)
|
28 |
*
|
28 |
*
|
29 |
* Whitespace around name and value is insignificant.
|
29 |
* Whitespace around name and value is insignificant.
|
30 |
*
|
30 |
*
|
31 |
* The names are case-sensitive but don't depend on it, this might change
|
31 |
* The names are case-sensitive but don't depend on it, this might change
|
32 |
*
|
32 |
*
|
33 |
* Values can be queried for, or set.
|
33 |
* Values can be queried for, or set.
|
34 |
*
|
34 |
*
|
35 |
* Any line without a '=' is a comment (a line like #var = value
|
35 |
* Any line without a '=' is a comment (a line like #var = value
|
36 |
* actually assigns a variable named '#var', which is not a big issue)
|
36 |
* actually assigns a variable named '#var', which is not a big issue)
|
37 |
*
|
37 |
*
|
38 |
* A configuration object can be created empty or by reading from a file or
|
38 |
* A configuration object can be created empty or by reading from a file or
|
39 |
* a string.
|
39 |
* a string.
|
40 |
* All 'set' calls cause an immediate rewrite of the backing object if any
|
40 |
* All 'set' calls cause an immediate rewrite of the backing object if any
|
41 |
* (file or string)
|
41 |
* (file or string)
|
42 |
*
|
42 |
*
|
43 |
* The ConfTree derived class interprets the subkeys as file paths and
|
43 |
* The ConfTree derived class interprets the subkeys as file paths and
|
44 |
* lets subdir keys hierarchically inherit the properties from
|
44 |
* lets subdir keys hierarchically inherit the properties from
|
45 |
* parents.
|
45 |
* parents.
|
46 |
*
|
46 |
*
|
47 |
* The ConfStack class stacks several Con(Simple/Tree) objects so that
|
47 |
* The ConfStack class stacks several Con(Simple/Tree) objects so that
|
48 |
* parameters from the top of the stack override the values from lower
|
48 |
* parameters from the top of the stack override the values from lower
|
49 |
* (useful to have central/personal config files)
|
49 |
* (useful to have central/personal config files)
|
50 |
*/
|
50 |
*/
|
51 |
|
51 |
|
|
|
52 |
#include <algorithm>
|
|
|
53 |
#include <map>
|
52 |
#include <string>
|
54 |
#include <string>
|
53 |
#include <map>
|
|
|
54 |
#include <vector>
|
55 |
#include <vector>
|
55 |
#include <algorithm>
|
|
|
56 |
|
56 |
|
57 |
// rh7.3 likes iostream better...
|
57 |
// rh7.3 likes iostream better...
|
58 |
#if defined(__GNUC__) && __GNUC__ < 3
|
58 |
#if defined(__GNUC__) && __GNUC__ < 3
|
59 |
#include <iostream>
|
59 |
#include <iostream>
|
60 |
#else
|
60 |
#else
|
61 |
#include <istream>
|
61 |
#include <istream>
|
62 |
#include <ostream>
|
62 |
#include <ostream>
|
63 |
#endif
|
63 |
#endif
|
64 |
|
64 |
|
65 |
#ifndef NO_NAMESPACES
|
65 |
#include "pathut.h"
|
|
|
66 |
|
66 |
using std::string;
|
67 |
using std::string;
|
67 |
using std::vector;
|
68 |
using std::vector;
|
68 |
using std::map;
|
69 |
using std::map;
|
69 |
using std::istream;
|
70 |
using std::istream;
|
70 |
using std::ostream;
|
71 |
using std::ostream;
|
71 |
#endif // NO_NAMESPACES
|
|
|
72 |
|
|
|
73 |
#include "pathut.h"
|
|
|
74 |
|
72 |
|
75 |
/** Internal class used for storing presentation information */
|
73 |
/** Internal class used for storing presentation information */
|
76 |
class ConfLine {
|
74 |
class ConfLine {
|
77 |
public:
|
75 |
public:
|
78 |
enum Kind {CFL_COMMENT, CFL_SK, CFL_VAR};
|
76 |
enum Kind {CFL_COMMENT, CFL_SK, CFL_VAR};
|
79 |
Kind m_kind;
|
77 |
Kind m_kind;
|
80 |
string m_data;
|
78 |
string m_data;
|
81 |
ConfLine(Kind k, const string& d)
|
79 |
ConfLine(Kind k, const string& d)
|
82 |
: m_kind(k), m_data(d)
|
80 |
: m_kind(k), m_data(d) {
|
83 |
{
|
81 |
}
|
84 |
}
|
|
|
85 |
bool operator==(const ConfLine& o)
|
82 |
bool operator==(const ConfLine& o) {
|
86 |
{
|
|
|
87 |
return o.m_kind == m_kind && o.m_data == m_data;
|
83 |
return o.m_kind == m_kind && o.m_data == m_data;
|
88 |
}
|
84 |
}
|
89 |
};
|
85 |
};
|
90 |
|
86 |
|
91 |
/**
|
87 |
/**
|
92 |
* Virtual base class used to define an interface mostly useful for testing
|
88 |
* Virtual base class used to define an interface mostly useful for testing
|
93 |
*/
|
89 |
*/
|
94 |
class ConfNull {
|
90 |
class ConfNull {
|
95 |
public:
|
91 |
public:
|
96 |
enum StatusCode {STATUS_ERROR=0, STATUS_RO=1, STATUS_RW=2};
|
92 |
enum StatusCode {STATUS_ERROR = 0, STATUS_RO = 1, STATUS_RW = 2};
|
97 |
virtual ~ConfNull() {};
|
93 |
virtual ~ConfNull() {};
|
98 |
virtual int get(const string &name, string &value,
|
94 |
virtual int get(const string& name, string& value,
|
99 |
const string &sk = string()) const = 0;
|
95 |
const string& sk = string()) const = 0;
|
100 |
virtual bool hasNameAnywhere(const string& nm) const = 0;
|
96 |
virtual bool hasNameAnywhere(const string& nm) const = 0;
|
101 |
virtual int set(const string &nm, const string &val,
|
97 |
virtual int set(const string& nm, const string& val,
|
102 |
const string &sk = string()) = 0;
|
98 |
const string& sk = string()) = 0;
|
103 |
virtual bool ok() const = 0;
|
99 |
virtual bool ok() const = 0;
|
104 |
virtual vector<string> getNames(const string &sk, const char* = 0)const = 0;
|
100 |
virtual vector<string> getNames(const string& sk, const char* = 0)const = 0;
|
105 |
virtual int erase(const string &, const string &) = 0;
|
101 |
virtual int erase(const string&, const string&) = 0;
|
106 |
virtual int eraseKey(const string &) = 0;
|
102 |
virtual int eraseKey(const string&) = 0;
|
107 |
virtual void showall() const {};
|
103 |
virtual void showall() const {};
|
108 |
virtual vector<string> getSubKeys() const = 0;
|
104 |
virtual vector<string> getSubKeys() const = 0;
|
109 |
virtual vector<string> getSubKeys(bool) const = 0;
|
105 |
virtual vector<string> getSubKeys(bool) const = 0;
|
110 |
virtual bool holdWrites(bool) = 0;
|
106 |
virtual bool holdWrites(bool) = 0;
|
111 |
virtual bool sourceChanged() const = 0;
|
107 |
virtual bool sourceChanged() const = 0;
|
112 |
};
|
108 |
};
|
113 |
|
109 |
|
114 |
/**
|
110 |
/**
|
115 |
* Manages a simple configuration file with subsections.
|
111 |
* Manages a simple configuration file with subsections.
|
116 |
*/
|
112 |
*/
|
117 |
class ConfSimple : public ConfNull {
|
113 |
class ConfSimple : public ConfNull {
|
118 |
public:
|
114 |
public:
|
119 |
|
115 |
|
|
... |
|
... |
125 |
*/
|
121 |
*/
|
126 |
ConfSimple(const char *fname, int readonly = 0, bool tildexp = false);
|
122 |
ConfSimple(const char *fname, int readonly = 0, bool tildexp = false);
|
127 |
|
123 |
|
128 |
/**
|
124 |
/**
|
129 |
* Build the object by reading content from a string
|
125 |
* Build the object by reading content from a string
|
130 |
* @param data points to the data to parse.
|
126 |
* @param data points to the data to parse.
|
131 |
* @param readonly if true open readonly, else rw
|
127 |
* @param readonly if true open readonly, else rw
|
132 |
* @param tildexp try tilde (home dir) expansion for subsection names
|
128 |
* @param tildexp try tilde (home dir) expansion for subsection names
|
133 |
*/
|
129 |
*/
|
134 |
ConfSimple(const string& data, int readonly = 0, bool tildexp = false);
|
130 |
ConfSimple(const string& data, int readonly = 0, bool tildexp = false);
|
135 |
|
131 |
|
|
... |
|
... |
143 |
virtual ~ConfSimple() {};
|
139 |
virtual ~ConfSimple() {};
|
144 |
|
140 |
|
145 |
/** Origin file changed. Only makes sense if we read the data from a file */
|
141 |
/** Origin file changed. Only makes sense if we read the data from a file */
|
146 |
virtual bool sourceChanged() const;
|
142 |
virtual bool sourceChanged() const;
|
147 |
|
143 |
|
148 |
/**
|
144 |
/**
|
149 |
* Decide if we actually rewrite the backing-store after modifying the
|
145 |
* Decide if we actually rewrite the backing-store after modifying the
|
150 |
* tree.
|
146 |
* tree.
|
151 |
*/
|
147 |
*/
|
152 |
virtual bool holdWrites(bool on)
|
148 |
virtual bool holdWrites(bool on) {
|
153 |
{
|
|
|
154 |
m_holdWrites = on;
|
149 |
m_holdWrites = on;
|
155 |
if (on == false) {
|
150 |
if (on == false) {
|
156 |
return write();
|
151 |
return write();
|
157 |
} else
|
152 |
} else {
|
158 |
return true;
|
153 |
return true;
|
|
|
154 |
}
|
159 |
}
|
155 |
}
|
160 |
|
156 |
|
161 |
/** Clear, then reparse from string */
|
157 |
/** Clear, then reparse from string */
|
162 |
void reparse(const string& in);
|
158 |
void reparse(const string& in);
|
163 |
|
159 |
|
164 |
/** Clear all content */
|
160 |
/** Clear all content */
|
165 |
void clear()
|
161 |
void clear() {
|
|
|
162 |
m_submaps.clear();
|
|
|
163 |
m_order.clear();
|
166 |
{
|
164 |
}
|
167 |
m_submaps.clear();
|
|
|
168 |
m_order.clear();
|
|
|
169 |
}
|
|
|
170 |
|
165 |
|
171 |
/**
|
166 |
/**
|
172 |
* Get value for named parameter, from specified subsection (looks in
|
167 |
* Get value for named parameter, from specified subsection (looks in
|
173 |
* global space if sk is empty).
|
168 |
* global space if sk is empty).
|
174 |
* @return 0 if name not found, 1 else
|
169 |
* @return 0 if name not found, 1 else
|
175 |
*/
|
170 |
*/
|
176 |
virtual int get(const string &name, string &value,
|
171 |
virtual int get(const string& name, string& value,
|
177 |
const string &sk = string()) const;
|
172 |
const string& sk = string()) const;
|
178 |
|
173 |
|
179 |
/**
|
174 |
/**
|
180 |
* Set value for named parameter in specified subsection (or global)
|
175 |
* Set value for named parameter in specified subsection (or global)
|
181 |
* @return 0 for error, 1 else
|
176 |
* @return 0 for error, 1 else
|
182 |
*/
|
177 |
*/
|
183 |
virtual int set(const string &nm, const string &val,
|
178 |
virtual int set(const string& nm, const string& val,
|
184 |
const string &sk = string());
|
179 |
const string& sk = string());
|
185 |
|
180 |
|
186 |
/**
|
181 |
/**
|
187 |
* Remove name and value from config
|
182 |
* Remove name and value from config
|
188 |
*/
|
183 |
*/
|
189 |
virtual int erase(const string &name, const string &sk);
|
184 |
virtual int erase(const string& name, const string& sk);
|
190 |
|
185 |
|
191 |
/**
|
186 |
/**
|
192 |
* Erase all names under given subkey (and subkey itself)
|
187 |
* Erase all names under given subkey (and subkey itself)
|
193 |
*/
|
188 |
*/
|
194 |
virtual int eraseKey(const string &sk);
|
189 |
virtual int eraseKey(const string& sk);
|
195 |
|
190 |
|
196 |
virtual StatusCode getStatus() const;
|
191 |
virtual StatusCode getStatus() const;
|
197 |
virtual bool ok() const {return getStatus() != STATUS_ERROR;}
|
192 |
virtual bool ok() const {
|
|
|
193 |
return getStatus() != STATUS_ERROR;
|
|
|
194 |
}
|
198 |
|
195 |
|
199 |
/**
|
196 |
/**
|
200 |
* Walk the configuration values, calling function for each.
|
197 |
* Walk the configuration values, calling function for each.
|
201 |
* The function is called with a null nm when changing subsections (the
|
198 |
* The function is called with a null nm when changing subsections (the
|
202 |
* value is then the new subsection name)
|
199 |
* value is then the new subsection name)
|
203 |
* @return WALK_STOP when/if the callback returns WALK_STOP,
|
200 |
* @return WALK_STOP when/if the callback returns WALK_STOP,
|
204 |
* WALK_CONTINUE else (got to end of config)
|
201 |
* WALK_CONTINUE else (got to end of config)
|
205 |
*/
|
202 |
*/
|
206 |
enum WalkerCode {WALK_STOP, WALK_CONTINUE};
|
203 |
enum WalkerCode {WALK_STOP, WALK_CONTINUE};
|
207 |
virtual WalkerCode sortwalk(WalkerCode
|
204 |
virtual WalkerCode sortwalk(WalkerCode
|
208 |
(*wlkr)(void *cldata, const string &nm,
|
205 |
(*wlkr)(void *cldata, const string& nm,
|
209 |
const string &val),
|
206 |
const string& val),
|
210 |
void *clidata) const;
|
207 |
void *clidata) const;
|
211 |
|
208 |
|
212 |
/** Print all values to stdout */
|
209 |
/** Print all values to stdout */
|
213 |
virtual void showall() const;
|
210 |
virtual void showall() const;
|
214 |
|
211 |
|
215 |
/** Return all names in given submap. */
|
212 |
/** Return all names in given submap. */
|
216 |
virtual vector<string> getNames(const string &sk, const char *pattern = 0)
|
213 |
virtual vector<string> getNames(const string& sk, const char *pattern = 0)
|
217 |
const;
|
214 |
const;
|
218 |
|
215 |
|
219 |
/** Check if name is present in any submap. This is relatively expensive
|
216 |
/** Check if name is present in any submap. This is relatively expensive
|
220 |
* but useful for saving further processing sometimes */
|
217 |
* but useful for saving further processing sometimes */
|
221 |
virtual bool hasNameAnywhere(const string& nm) const;
|
218 |
virtual bool hasNameAnywhere(const string& nm) const;
|
222 |
|
219 |
|
223 |
/**
|
220 |
/**
|
224 |
* Return all subkeys
|
221 |
* Return all subkeys
|
225 |
*/
|
222 |
*/
|
226 |
virtual vector<string> getSubKeys(bool) const
|
223 |
virtual vector<string> getSubKeys(bool) const {
|
|
|
224 |
return getSubKeys();
|
227 |
{
|
225 |
}
|
228 |
return getSubKeys();
|
226 |
virtual vector<string> getSubKeys_unsorted(bool = false) const {
|
|
|
227 |
return m_subkeys_unsorted;
|
229 |
}
|
228 |
}
|
230 |
virtual vector<string> getSubKeys() const;
|
229 |
virtual vector<string> getSubKeys() const;
|
231 |
/** Test for subkey existence */
|
230 |
/** Test for subkey existence */
|
232 |
virtual bool hasSubKey(const string& sk) const
|
231 |
virtual bool hasSubKey(const string& sk) const {
|
233 |
{
|
|
|
234 |
return m_submaps.find(sk) != m_submaps.end();
|
232 |
return m_submaps.find(sk) != m_submaps.end();
|
235 |
}
|
233 |
}
|
236 |
|
234 |
|
237 |
virtual string getFilename() const
|
235 |
virtual string getFilename() const {
|
238 |
{return m_filename;}
|
236 |
return m_filename;
|
|
|
237 |
}
|
239 |
|
238 |
|
240 |
/**
|
239 |
/**
|
241 |
* Copy constructor. Expensive but less so than a full rebuild
|
240 |
* Copy constructor. Expensive but less so than a full rebuild
|
242 |
*/
|
241 |
*/
|
243 |
ConfSimple(const ConfSimple &rhs)
|
242 |
ConfSimple(const ConfSimple& rhs)
|
244 |
: ConfNull()
|
243 |
: ConfNull() {
|
245 |
{
|
|
|
246 |
if ((status = rhs.status) == STATUS_ERROR)
|
244 |
if ((status = rhs.status) == STATUS_ERROR) {
|
247 |
return;
|
245 |
return;
|
|
|
246 |
}
|
248 |
m_filename = rhs.m_filename;
|
247 |
m_filename = rhs.m_filename;
|
249 |
m_submaps = rhs.m_submaps;
|
248 |
m_submaps = rhs.m_submaps;
|
250 |
}
|
249 |
}
|
251 |
|
250 |
|
252 |
/**
|
251 |
/**
|
253 |
* Assignement. This is expensive
|
252 |
* Assignement. This is expensive
|
254 |
*/
|
253 |
*/
|
255 |
ConfSimple& operator=(const ConfSimple &rhs)
|
254 |
ConfSimple& operator=(const ConfSimple& rhs) {
|
256 |
{
|
|
|
257 |
if (this != &rhs && (status = rhs.status) != STATUS_ERROR) {
|
255 |
if (this != &rhs && (status = rhs.status) != STATUS_ERROR) {
|
258 |
m_filename = rhs.m_filename;
|
256 |
m_filename = rhs.m_filename;
|
259 |
m_submaps = rhs.m_submaps;
|
257 |
m_submaps = rhs.m_submaps;
|
260 |
}
|
258 |
}
|
261 |
return *this;
|
259 |
return *this;
|
262 |
}
|
260 |
}
|
263 |
|
261 |
|
264 |
/**
|
262 |
/**
|
265 |
* Write in file format to out
|
263 |
* Write in file format to out
|
266 |
*/
|
264 |
*/
|
|
... |
|
... |
269 |
protected:
|
267 |
protected:
|
270 |
bool dotildexpand;
|
268 |
bool dotildexpand;
|
271 |
StatusCode status;
|
269 |
StatusCode status;
|
272 |
private:
|
270 |
private:
|
273 |
// Set if we're working with a file
|
271 |
// Set if we're working with a file
|
274 |
string m_filename;
|
272 |
string m_filename;
|
275 |
time_t m_fmtime;
|
273 |
time_t m_fmtime;
|
276 |
// Configuration data submaps (one per subkey, the main data has a
|
274 |
// Configuration data submaps (one per subkey, the main data has a
|
277 |
// null subkey)
|
275 |
// null subkey)
|
278 |
map<string, map<string, string> > m_submaps;
|
276 |
map<string, map<string, string> > m_submaps;
|
|
|
277 |
vector<string> m_subkeys_unsorted;
|
279 |
// Presentation data. We keep the comments, empty lines and
|
278 |
// Presentation data. We keep the comments, empty lines and
|
280 |
// variable and subkey ordering information in there (for
|
279 |
// variable and subkey ordering information in there (for
|
281 |
// rewriting the file while keeping hand-edited information)
|
280 |
// rewriting the file while keeping hand-edited information)
|
282 |
vector<ConfLine> m_order;
|
281 |
vector<ConfLine> m_order;
|
283 |
// Control if we're writing to the backing store
|
282 |
// Control if we're writing to the backing store
|
284 |
bool m_holdWrites;
|
283 |
bool m_holdWrites;
|
285 |
|
284 |
|
286 |
void parseinput(istream& input);
|
285 |
void parseinput(istream& input);
|
287 |
bool write();
|
286 |
bool write();
|
288 |
// Internal version of set: no RW checking
|
287 |
// Internal version of set: no RW checking
|
289 |
virtual int i_set(const string &nm, const string &val,
|
288 |
virtual int i_set(const string& nm, const string& val,
|
290 |
const string &sk, bool init = false);
|
289 |
const string& sk, bool init = false);
|
291 |
bool i_changed(bool upd);
|
290 |
bool i_changed(bool upd);
|
292 |
};
|
291 |
};
|
293 |
|
292 |
|
294 |
/**
|
293 |
/**
|
295 |
* This is a configuration class which attaches tree-like signification to the
|
294 |
* This is a configuration class which attaches tree-like signification to the
|
296 |
* submap names.
|
295 |
* submap names.
|
297 |
*
|
296 |
*
|
298 |
* If a given variable is not found in the specified section, it will be
|
297 |
* If a given variable is not found in the specified section, it will be
|
299 |
* looked up the tree of section names, and in the global space.
|
298 |
* looked up the tree of section names, and in the global space.
|
300 |
*
|
299 |
*
|
301 |
* submap names should be '/' separated paths (ie: /sub1/sub2). No checking
|
300 |
* submap names should be '/' separated paths (ie: /sub1/sub2). No checking
|
302 |
* is done, but else the class adds no functionality to ConfSimple.
|
301 |
* is done, but else the class adds no functionality to ConfSimple.
|
303 |
*
|
302 |
*
|
304 |
* NOTE: contrary to common behaviour, the global or root space is NOT
|
303 |
* NOTE: contrary to common behaviour, the global or root space is NOT
|
305 |
* designated by '/' but by '' (empty subkey). A '/' subkey will not
|
304 |
* designated by '/' but by '' (empty subkey). A '/' subkey will not
|
306 |
* be searched at all.
|
305 |
* be searched at all.
|
307 |
*
|
306 |
*
|
308 |
* Note: getNames() : uses ConfSimple method, this does *not* inherit
|
307 |
* Note: getNames() : uses ConfSimple method, this does *not* inherit
|
309 |
* names from englobing submaps.
|
308 |
* names from englobing submaps.
|
310 |
*/
|
309 |
*/
|
311 |
class ConfTree : public ConfSimple {
|
310 |
class ConfTree : public ConfSimple {
|
312 |
|
311 |
|
313 |
public:
|
312 |
public:
|
314 |
/* The constructors just call ConfSimple's, asking for key tilde
|
313 |
/* The constructors just call ConfSimple's, asking for key tilde
|
315 |
* expansion */
|
314 |
* expansion */
|
316 |
ConfTree(const char *fname, int readonly = 0)
|
315 |
ConfTree(const char *fname, int readonly = 0)
|
317 |
: ConfSimple(fname, readonly, true) {}
|
316 |
: ConfSimple(fname, readonly, true) {}
|
318 |
ConfTree(const string &data, int readonly = 0)
|
317 |
ConfTree(const string& data, int readonly = 0)
|
319 |
: ConfSimple(data, readonly, true) {}
|
318 |
: ConfSimple(data, readonly, true) {}
|
320 |
ConfTree(int readonly = 0)
|
319 |
ConfTree(int readonly = 0)
|
321 |
: ConfSimple(readonly, true) {}
|
320 |
: ConfSimple(readonly, true) {}
|
322 |
virtual ~ConfTree() {};
|
321 |
virtual ~ConfTree() {};
|
323 |
ConfTree(const ConfTree& r) : ConfSimple(r) {};
|
322 |
ConfTree(const ConfTree& r) : ConfSimple(r) {};
|
324 |
ConfTree& operator=(const ConfTree& r)
|
323 |
ConfTree& operator=(const ConfTree& r) {
|
325 |
{
|
|
|
326 |
ConfSimple::operator=(r);
|
324 |
ConfSimple::operator=(r);
|
327 |
return *this;
|
325 |
return *this;
|
328 |
}
|
326 |
}
|
329 |
|
327 |
|
330 |
/**
|
328 |
/**
|
331 |
* Get value for named parameter, from specified subsection, or its
|
329 |
* Get value for named parameter, from specified subsection, or its
|
332 |
* parents.
|
330 |
* parents.
|
333 |
* @return 0 if name not found, 1 else
|
331 |
* @return 0 if name not found, 1 else
|
334 |
*/
|
332 |
*/
|
335 |
virtual int get(const string &name, string &value, const string &sk) const;
|
333 |
virtual int get(const string& name, string& value, const string& sk) const;
|
336 |
};
|
334 |
};
|
337 |
|
335 |
|
338 |
/**
|
336 |
/**
|
339 |
* Use several config files, trying to get values from each in order. Used to
|
337 |
* Use several config files, trying to get values from each in order. Used to
|
340 |
* have a central config, with possible overrides from more specific
|
338 |
* have a central config, with possible overrides from more specific
|
341 |
* (ie personal) ones.
|
339 |
* (ie personal) ones.
|
342 |
*
|
340 |
*
|
343 |
* Notes: it's ok for some of the files not to exist, but the last
|
341 |
* Notes: it's ok for some of the files not to exist, but the last
|
344 |
* one must or we generate an error. We open all trees readonly, except the
|
342 |
* one must or we generate an error. We open all trees readonly, except the
|
345 |
* topmost one if requested. All writes go to the topmost file. Note that
|
343 |
* topmost one if requested. All writes go to the topmost file. Note that
|
346 |
* erase() won't work except for parameters only defined in the topmost
|
344 |
* erase() won't work except for parameters only defined in the topmost
|
347 |
* file (it erases only from there).
|
345 |
* file (it erases only from there).
|
348 |
*/
|
346 |
*/
|
349 |
template <class T> class ConfStack : public ConfNull {
|
347 |
template <class T> class ConfStack : public ConfNull {
|
350 |
public:
|
348 |
public:
|
351 |
/// Construct from configuration file names. The earler
|
349 |
/// Construct from configuration file names. The earler
|
352 |
/// files in have priority when fetching values. Only the first
|
350 |
/// files in have priority when fetching values. Only the first
|
353 |
/// file will be updated if ro is false and set() is used.
|
351 |
/// file will be updated if ro is false and set() is used.
|
354 |
ConfStack(const vector<string> &fns, bool ro = true)
|
352 |
ConfStack(const vector<string>& fns, bool ro = true) {
|
355 |
{
|
|
|
356 |
construct(fns, ro);
|
353 |
construct(fns, ro);
|
357 |
}
|
354 |
}
|
358 |
/// Construct out of single file name and multiple directories
|
355 |
/// Construct out of single file name and multiple directories
|
359 |
ConfStack(const string& nm, const vector<string>& dirs, bool ro = true)
|
356 |
ConfStack(const string& nm, const vector<string>& dirs, bool ro = true) {
|
360 |
{
|
|
|
361 |
vector<string> fns;
|
357 |
vector<string> fns;
|
362 |
for (vector<string>::const_iterator it = dirs.begin();
|
358 |
for (vector<string>::const_iterator it = dirs.begin();
|
363 |
it != dirs.end(); it++){
|
359 |
it != dirs.end(); it++) {
|
364 |
fns.push_back(path_cat(*it, nm));
|
360 |
fns.push_back(path_cat(*it, nm));
|
365 |
}
|
361 |
}
|
366 |
ConfStack::construct(fns, ro);
|
362 |
ConfStack::construct(fns, ro);
|
367 |
}
|
363 |
}
|
368 |
|
364 |
|
369 |
ConfStack(const ConfStack &rhs)
|
365 |
ConfStack(const ConfStack& rhs)
|
370 |
: ConfNull()
|
366 |
: ConfNull() {
|
|
|
367 |
init_from(rhs);
|
371 |
{
|
368 |
}
|
372 |
init_from(rhs);
|
|
|
373 |
}
|
|
|
374 |
|
369 |
|
375 |
virtual ~ConfStack()
|
370 |
virtual ~ConfStack() {
|
|
|
371 |
clear();
|
|
|
372 |
m_ok = false;
|
376 |
{
|
373 |
}
|
377 |
clear();
|
|
|
378 |
m_ok = false;
|
|
|
379 |
}
|
|
|
380 |
|
374 |
|
381 |
ConfStack& operator=(const ConfStack &rhs)
|
375 |
ConfStack& operator=(const ConfStack& rhs) {
|
|
|
376 |
if (this != &rhs) {
|
|
|
377 |
clear();
|
|
|
378 |
m_ok = rhs.m_ok;
|
|
|
379 |
if (m_ok) {
|
|
|
380 |
init_from(rhs);
|
|
|
381 |
}
|
|
|
382 |
}
|
|
|
383 |
return *this;
|
382 |
{
|
384 |
}
|
383 |
if (this != &rhs){
|
|
|
384 |
clear();
|
|
|
385 |
m_ok = rhs.m_ok;
|
|
|
386 |
if (m_ok)
|
|
|
387 |
init_from(rhs);
|
|
|
388 |
}
|
|
|
389 |
return *this;
|
|
|
390 |
}
|
|
|
391 |
|
385 |
|
392 |
virtual bool sourceChanged() const
|
386 |
virtual bool sourceChanged() const {
|
393 |
{
|
|
|
394 |
typename vector<T*>::const_iterator it;
|
387 |
typename vector<T*>::const_iterator it;
|
395 |
for (it = m_confs.begin();it != m_confs.end();it++) {
|
388 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
396 |
if ((*it)->sourceChanged())
|
389 |
if ((*it)->sourceChanged()) {
|
397 |
return true;
|
390 |
return true;
|
398 |
}
|
391 |
}
|
|
|
392 |
}
|
399 |
return false;
|
393 |
return false;
|
400 |
}
|
394 |
}
|
401 |
|
395 |
|
402 |
virtual int get(const string &name, string &value, const string &sk,
|
396 |
virtual int get(const string& name, string& value, const string& sk,
|
403 |
bool shallow) const
|
397 |
bool shallow) const {
|
404 |
{
|
|
|
405 |
typename vector<T*>::const_iterator it;
|
398 |
typename vector<T*>::const_iterator it;
|
406 |
for (it = m_confs.begin();it != m_confs.end();it++) {
|
399 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
407 |
if ((*it)->get(name, value, sk))
|
400 |
if ((*it)->get(name, value, sk)) {
|
408 |
return true;
|
401 |
return true;
|
|
|
402 |
}
|
409 |
if (shallow)
|
403 |
if (shallow) {
|
410 |
break;
|
404 |
break;
|
411 |
}
|
405 |
}
|
|
|
406 |
}
|
412 |
return false;
|
407 |
return false;
|
413 |
}
|
408 |
}
|
414 |
virtual int get(const string &name, string &value, const string &sk) const {
|
409 |
virtual int get(const string& name, string& value, const string& sk) const {
|
415 |
return get(name, value, sk, false);
|
410 |
return get(name, value, sk, false);
|
416 |
}
|
411 |
}
|
417 |
|
412 |
|
418 |
virtual bool hasNameAnywhere(const string& nm) const
|
413 |
virtual bool hasNameAnywhere(const string& nm) const {
|
419 |
{
|
|
|
420 |
typename vector<T*>::const_iterator it;
|
414 |
typename vector<T*>::const_iterator it;
|
421 |
for (it = m_confs.begin();it != m_confs.end();it++) {
|
415 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
422 |
if ((*it)->hasNameAnywhere(nm))
|
416 |
if ((*it)->hasNameAnywhere(nm)) {
|
423 |
return true;
|
417 |
return true;
|
424 |
}
|
418 |
}
|
|
|
419 |
}
|
425 |
return false;
|
420 |
return false;
|
426 |
}
|
421 |
}
|
427 |
|
422 |
|
428 |
virtual int set(const string &nm, const string &val,
|
423 |
virtual int set(const string& nm, const string& val,
|
429 |
const string &sk = string())
|
424 |
const string& sk = string()) {
|
430 |
{
|
425 |
if (!m_ok) {
|
431 |
if (!m_ok)
|
|
|
432 |
return 0;
|
426 |
return 0;
|
|
|
427 |
}
|
433 |
//LOGDEB2(("ConfStack::set [%s]:[%s] -> [%s]\n", sk.c_str(),
|
428 |
//LOGDEB2(("ConfStack::set [%s]:[%s] -> [%s]\n", sk.c_str(),
|
434 |
//nm.c_str(), val.c_str()));
|
429 |
//nm.c_str(), val.c_str()));
|
435 |
// Avoid adding unneeded entries: if the new value matches the
|
430 |
// Avoid adding unneeded entries: if the new value matches the
|
436 |
// one out from the deeper configs, erase or dont add it
|
431 |
// one out from the deeper configs, erase or dont add it
|
437 |
// from/to the topmost file
|
432 |
// from/to the topmost file
|
438 |
typename vector<T*>::iterator it = m_confs.begin();
|
433 |
typename vector<T*>::iterator it = m_confs.begin();
|
439 |
it++;
|
434 |
it++;
|
440 |
while (it != m_confs.end()) {
|
435 |
while (it != m_confs.end()) {
|
441 |
string value;
|
436 |
string value;
|
442 |
if ((*it)->get(nm, value, sk)) {
|
437 |
if ((*it)->get(nm, value, sk)) {
|
443 |
// This file has value for nm/sk. If it is the same as the new
|
438 |
// This file has value for nm/sk. If it is the same as the new
|
444 |
// one, no need for an entry in the topmost file. Else, stop
|
439 |
// one, no need for an entry in the topmost file. Else, stop
|
445 |
// looking and add the new entry
|
440 |
// looking and add the new entry
|
446 |
if (value == val) {
|
441 |
if (value == val) {
|
447 |
m_confs.front()->erase(nm, sk);
|
442 |
m_confs.front()->erase(nm, sk);
|
448 |
return true;
|
443 |
return true;
|
449 |
} else {
|
444 |
} else {
|
450 |
break;
|
445 |
break;
|
451 |
}
|
446 |
}
|
|
|
447 |
}
|
|
|
448 |
it++;
|
|
|
449 |
}
|
|
|
450 |
|
|
|
451 |
return m_confs.front()->set(nm, val, sk);
|
452 |
}
|
452 |
}
|
453 |
it++;
|
|
|
454 |
}
|
|
|
455 |
|
453 |
|
456 |
return m_confs.front()->set(nm, val, sk);
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
virtual int erase(const string &nm, const string &sk)
|
454 |
virtual int erase(const string& nm, const string& sk) {
|
460 |
{
|
|
|
461 |
return m_confs.front()->erase(nm, sk);
|
455 |
return m_confs.front()->erase(nm, sk);
|
462 |
}
|
456 |
}
|
463 |
virtual int eraseKey(const string &sk)
|
457 |
virtual int eraseKey(const string& sk) {
|
464 |
{
|
|
|
465 |
return m_confs.front()->eraseKey(sk);
|
458 |
return m_confs.front()->eraseKey(sk);
|
466 |
}
|
459 |
}
|
467 |
virtual bool holdWrites(bool on)
|
460 |
virtual bool holdWrites(bool on) {
|
468 |
{
|
|
|
469 |
return m_confs.front()->holdWrites(on);
|
461 |
return m_confs.front()->holdWrites(on);
|
470 |
}
|
462 |
}
|
471 |
|
463 |
|
472 |
virtual vector<string> getNames(const string &sk, const char *pattern = 0)
|
464 |
virtual vector<string> getNames(const string& sk, const char *pattern = 0)
|
473 |
const
|
465 |
const {
|
474 |
{
|
|
|
475 |
return getNames1(sk, pattern, false);
|
466 |
return getNames1(sk, pattern, false);
|
476 |
}
|
467 |
}
|
477 |
virtual vector<string> getNamesShallow(const string &sk,
|
468 |
virtual vector<string> getNamesShallow(const string& sk,
|
478 |
const char *patt = 0) const
|
469 |
const char *patt = 0) const {
|
479 |
{
|
|
|
480 |
return getNames1(sk, patt, true);
|
470 |
return getNames1(sk, patt, true);
|
481 |
}
|
471 |
}
|
482 |
|
472 |
|
483 |
virtual vector<string> getNames1(const string &sk, const char *pattern,
|
473 |
virtual vector<string> getNames1(const string& sk, const char *pattern,
|
484 |
bool shallow) const
|
474 |
bool shallow) const {
|
485 |
{
|
|
|
486 |
vector<string> nms;
|
475 |
vector<string> nms;
|
487 |
typename vector<T*>::const_iterator it;
|
476 |
typename vector<T*>::const_iterator it;
|
488 |
bool skfound = false;
|
477 |
bool skfound = false;
|
489 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
478 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
490 |
if ((*it)->hasSubKey(sk)) {
|
479 |
if ((*it)->hasSubKey(sk)) {
|
491 |
skfound = true;
|
480 |
skfound = true;
|
492 |
vector<string> lst = (*it)->getNames(sk, pattern);
|
481 |
vector<string> lst = (*it)->getNames(sk, pattern);
|
493 |
nms.insert(nms.end(), lst.begin(), lst.end());
|
482 |
nms.insert(nms.end(), lst.begin(), lst.end());
|
494 |
}
|
483 |
}
|
495 |
if (shallow && skfound)
|
484 |
if (shallow && skfound) {
|
496 |
break;
|
485 |
break;
|
497 |
}
|
486 |
}
|
|
|
487 |
}
|
498 |
sort(nms.begin(), nms.end());
|
488 |
sort(nms.begin(), nms.end());
|
499 |
vector<string>::iterator uit = unique(nms.begin(), nms.end());
|
489 |
vector<string>::iterator uit = unique(nms.begin(), nms.end());
|
500 |
nms.resize(uit - nms.begin());
|
490 |
nms.resize(uit - nms.begin());
|
501 |
return nms;
|
491 |
return nms;
|
502 |
}
|
492 |
}
|
503 |
|
493 |
|
504 |
virtual vector<string> getSubKeys() const
|
494 |
virtual vector<string> getSubKeys() const {
|
505 |
{
|
|
|
506 |
return getSubKeys(false);
|
495 |
return getSubKeys(false);
|
507 |
}
|
496 |
}
|
508 |
virtual vector<string> getSubKeys(bool shallow) const
|
497 |
virtual vector<string> getSubKeys(bool shallow) const {
|
509 |
{
|
|
|
510 |
vector<string> sks;
|
498 |
vector<string> sks;
|
511 |
typename vector<T*>::const_iterator it;
|
499 |
typename vector<T*>::const_iterator it;
|
512 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
500 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
513 |
vector<string> lst;
|
501 |
vector<string> lst;
|
514 |
lst = (*it)->getSubKeys();
|
502 |
lst = (*it)->getSubKeys();
|
515 |
sks.insert(sks.end(), lst.begin(), lst.end());
|
503 |
sks.insert(sks.end(), lst.begin(), lst.end());
|
516 |
if (shallow)
|
504 |
if (shallow) {
|
517 |
break;
|
505 |
break;
|
518 |
}
|
506 |
}
|
|
|
507 |
}
|
519 |
sort(sks.begin(), sks.end());
|
508 |
sort(sks.begin(), sks.end());
|
520 |
vector<string>::iterator uit = unique(sks.begin(), sks.end());
|
509 |
vector<string>::iterator uit = unique(sks.begin(), sks.end());
|
521 |
sks.resize(uit - sks.begin());
|
510 |
sks.resize(uit - sks.begin());
|
522 |
return sks;
|
511 |
return sks;
|
523 |
}
|
512 |
}
|
524 |
|
513 |
|
525 |
virtual bool ok() const {return m_ok;}
|
514 |
virtual bool ok() const {
|
|
|
515 |
return m_ok;
|
|
|
516 |
}
|
526 |
|
517 |
|
527 |
private:
|
518 |
private:
|
528 |
bool m_ok;
|
519 |
bool m_ok;
|
529 |
vector<T*> m_confs;
|
520 |
vector<T*> m_confs;
|
530 |
|
521 |
|
531 |
/// Reset to pristine
|
522 |
/// Reset to pristine
|
532 |
void clear() {
|
523 |
void clear() {
|
533 |
typename vector<T*>::iterator it;
|
524 |
typename vector<T*>::iterator it;
|
534 |
for (it = m_confs.begin();it != m_confs.end();it++) {
|
525 |
for (it = m_confs.begin(); it != m_confs.end(); it++) {
|
535 |
delete (*it);
|
526 |
delete(*it);
|
536 |
}
|
527 |
}
|
537 |
m_confs.clear();
|
528 |
m_confs.clear();
|
538 |
}
|
529 |
}
|
539 |
|
530 |
|
540 |
/// Common code to initialize from existing object
|
531 |
/// Common code to initialize from existing object
|
541 |
void init_from(const ConfStack &rhs) {
|
532 |
void init_from(const ConfStack& rhs) {
|
542 |
if ((m_ok = rhs.m_ok)) {
|
533 |
if ((m_ok = rhs.m_ok)) {
|
543 |
typename vector<T*>::const_iterator it;
|
534 |
typename vector<T*>::const_iterator it;
|
544 |
for (it = rhs.m_confs.begin();it != rhs.m_confs.end();it++) {
|
535 |
for (it = rhs.m_confs.begin(); it != rhs.m_confs.end(); it++) {
|
545 |
m_confs.push_back(new T(**it));
|
536 |
m_confs.push_back(new T(**it));
|
546 |
}
|
537 |
}
|
547 |
}
|
538 |
}
|
548 |
}
|
539 |
}
|
549 |
|
540 |
|
550 |
/// Common construct from file names code
|
541 |
/// Common construct from file names code
|
551 |
void construct(const vector<string> &fns, bool ro) {
|
542 |
void construct(const vector<string>& fns, bool ro) {
|
552 |
vector<string>::const_iterator it;
|
543 |
vector<string>::const_iterator it;
|
553 |
bool lastok = false;
|
544 |
bool lastok = false;
|
554 |
for (it = fns.begin(); it != fns.end(); it++) {
|
545 |
for (it = fns.begin(); it != fns.end(); it++) {
|
555 |
T* p = new T(it->c_str(), ro);
|
546 |
T* p = new T(it->c_str(), ro);
|
556 |
if (p && p->ok()) {
|
547 |
if (p && p->ok()) {
|
557 |
m_confs.push_back(p);
|
548 |
m_confs.push_back(p);
|
558 |
lastok = true;
|
549 |
lastok = true;
|
559 |
} else {
|
550 |
} else {
|
560 |
delete p;
|
551 |
delete p;
|
561 |
lastok = false;
|
552 |
lastok = false;
|
562 |
if (!ro) {
|
553 |
if (!ro) {
|
563 |
// For rw acccess, the topmost file needs to be ok
|
554 |
// For rw acccess, the topmost file needs to be ok
|
564 |
// (ro is set to true after the first file)
|
555 |
// (ro is set to true after the first file)
|
565 |
break;
|
556 |
break;
|
566 |
}
|
557 |
}
|
567 |
}
|
558 |
}
|
568 |
ro = true;
|
559 |
ro = true;
|
569 |
}
|
560 |
}
|
570 |
m_ok = lastok;
|
561 |
m_ok = lastok;
|
571 |
}
|
562 |
}
|
572 |
};
|
563 |
};
|
573 |
|
564 |
|
574 |
#endif /*_CONFTREE_H_ */
|
565 |
#endif /*_CONFTREE_H_ */
|