Switch to unified view

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