Switch to unified view

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