Switch to unified view

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_ */