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