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