Allow updates in confstacks
This commit is contained in:
parent
3ef569c5db
commit
96aff5eb29
@ -1,5 +1,5 @@
|
||||
#ifndef lint
|
||||
static char rcsid [] = "@(#$Id: conftree.cpp,v 1.9 2007-08-03 07:50:49 dockes Exp $ (C) 2003 J.F.Dockes";
|
||||
static char rcsid [] = "@(#$Id: conftree.cpp,v 1.10 2007-08-04 07:22:43 dockes Exp $ (C) 2003 J.F.Dockes";
|
||||
#endif
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -332,12 +332,6 @@ int ConfSimple::erase(const string &nm, const string &sk)
|
||||
return write();
|
||||
}
|
||||
|
||||
int ConfSimple::set(const char *nm, const char *value, const char *sk)
|
||||
{
|
||||
string ssk = (sk == 0) ? string("") : string(sk);
|
||||
return set(string(nm), string(value), ssk);
|
||||
}
|
||||
|
||||
ConfSimple::WalkerCode
|
||||
ConfSimple::sortwalk(WalkerCode (*walker)(void *,const string&,const string&),
|
||||
void *clidata)
|
||||
@ -538,11 +532,89 @@ void memtest(ConfSimple &c)
|
||||
c.listall();
|
||||
}
|
||||
|
||||
bool readwrite(ConfNull *conf)
|
||||
{
|
||||
if (conf->ok()) {
|
||||
// It's ok for the file to not exist here
|
||||
string value;
|
||||
|
||||
if (conf->get("mypid", value)) {
|
||||
cout << "Value for mypid is [" << value << "]" << endl;
|
||||
} else {
|
||||
cout << "mypid not set" << endl;
|
||||
}
|
||||
|
||||
if (conf->get("unstring", value)) {
|
||||
cout << "Value for unstring is ["<< value << "]" << endl;
|
||||
} else {
|
||||
cout << "unstring not set" << endl;
|
||||
}
|
||||
}
|
||||
char spid[100];
|
||||
sprintf(spid, "%d", getpid());
|
||||
if (!conf->set("mypid", spid)) {
|
||||
cerr << "Set mypid failed" << endl;
|
||||
}
|
||||
|
||||
ostringstream ost;
|
||||
ost << "mypid" << getpid();
|
||||
if (!conf->set(ost.str(), spid, "")) {
|
||||
cerr << "Set mypid failed (2)" << endl;
|
||||
}
|
||||
if (!conf->set("unstring", "Une jolie phrase pour essayer")) {
|
||||
cerr << "Set unstring failed" << endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool query(ConfNull *conf, const string& nm, const string& sub)
|
||||
{
|
||||
if (!conf->ok()) {
|
||||
cerr << "Error opening or parsing file\n" << endl;
|
||||
return false;
|
||||
}
|
||||
string value;
|
||||
if (!conf->get(nm, value, sub)) {
|
||||
cerr << "name [" << nm << "] not found in [" << sub << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
cout << "[" << sub << "] " << nm << " " << value << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool erase(ConfNull *conf, const string& nm, const string& sub)
|
||||
{
|
||||
if (!conf->ok()) {
|
||||
cerr << "Error opening or parsing file\n" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!conf->erase(nm, sub)) {
|
||||
cerr << "delete name [" << nm << "] in ["<< sub << "] failed" << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setvar(ConfNull *conf, const string& nm, const string& value,
|
||||
const string& sub)
|
||||
{
|
||||
if (!conf->ok()) {
|
||||
cerr << "Error opening or parsing file\n" << endl;
|
||||
return false;
|
||||
}
|
||||
if (!conf->set(nm, value, sub)) {
|
||||
cerr << "Set error\n" << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char usage [] =
|
||||
"testconftree [opts] filename\n"
|
||||
"[-w] : read/write test.\n"
|
||||
"[-s] : string parsing test. Filename must hold parm 'strings'\n"
|
||||
"[-a] nm value sect : add nm,value in 'sect' which can be ''\n"
|
||||
"[-a] nm value sect : add/set nm,value in 'sect' which can be ''\n"
|
||||
"[-q] nm sect : subsection test: look for nm in 'sect' which can be ''\n"
|
||||
"[-d] nm sect : delete nm in 'sect' which can be ''\n"
|
||||
"[-S] : string io test. No filename in this case\n"
|
||||
@ -562,6 +634,7 @@ static int op_flags;
|
||||
#define OPT_d 0x20
|
||||
#define OPT_V 0x40
|
||||
#define OPT_a 0x80
|
||||
#define OPT_k 0x100
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@ -602,6 +675,7 @@ int main(int argc, char **argv)
|
||||
sub = *(++argv);argc--;
|
||||
goto b1;
|
||||
case 's': op_flags |= OPT_s; break;
|
||||
case 'k': op_flags |= OPT_k; break;
|
||||
case 'S': op_flags |= OPT_S; break;
|
||||
case 'V': op_flags |= OPT_S; break;
|
||||
case 'w': op_flags |= OPT_w; break;
|
||||
@ -612,131 +686,100 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if ((op_flags & OPT_S)) {
|
||||
// String storage test
|
||||
if (argc != 0)
|
||||
Usage();
|
||||
string s;
|
||||
ConfSimple c(&s);
|
||||
memtest(c);
|
||||
exit(0);
|
||||
} else if ((op_flags & OPT_V)) {
|
||||
// No storage test
|
||||
if (argc != 0)
|
||||
Usage();
|
||||
string s;
|
||||
ConfSimple c;
|
||||
memtest(c);
|
||||
} else {
|
||||
if (argc < 1)
|
||||
Usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
const char *filename = *argv++;argc--;
|
||||
// Other tests use file(s) as backing store
|
||||
if (argc < 1)
|
||||
Usage();
|
||||
|
||||
if (op_flags & OPT_w) {
|
||||
ConfSimple parms(filename);
|
||||
if (parms.getStatus() != ConfSimple::STATUS_ERROR) {
|
||||
// It's ok for the file to not exist here
|
||||
string value;
|
||||
|
||||
if (parms.get("mypid", value)) {
|
||||
printf("Value for mypid is '%s'\n", value.c_str());
|
||||
} else {
|
||||
printf("mypid not set\n");
|
||||
}
|
||||
|
||||
if (parms.get("unstring", value)) {
|
||||
printf("Value for unstring is '%s'\n", value.c_str());
|
||||
} else {
|
||||
printf("unstring not set\n");
|
||||
}
|
||||
}
|
||||
char spid[100];
|
||||
sprintf(spid, "%d", getpid());
|
||||
if (!parms.set("mypid", spid)) {
|
||||
cerr << "Set mypid failed" << endl;
|
||||
}
|
||||
|
||||
ostringstream ost;;
|
||||
ost << "mypid" << getpid();
|
||||
if (!parms.set(ost.str(), spid, "")) {
|
||||
cerr << "Set mypid failed (2)" << endl;
|
||||
}
|
||||
if (!parms.set("unstring", "Une jolie phrase pour essayer")) {
|
||||
cerr << "Set unstring failed" << endl;
|
||||
}
|
||||
} else if (op_flags & OPT_q) {
|
||||
ConfTree parms(filename, 0);
|
||||
if (parms.getStatus() == ConfSimple::STATUS_ERROR) {
|
||||
fprintf(stderr, "Error opening or parsing file\n");
|
||||
exit(1);
|
||||
}
|
||||
string value;
|
||||
if (!parms.get(nm, value, sub)) {
|
||||
fprintf(stderr, "name '%s' not found in '%s'\n", nm, sub);
|
||||
exit(1);
|
||||
}
|
||||
printf("%s : '%s' = '%s'\n", sub, nm, value.c_str());
|
||||
exit(0);
|
||||
} else if (op_flags & OPT_a) {
|
||||
ConfTree parms(filename, 0);
|
||||
if (parms.getStatus() == ConfSimple::STATUS_ERROR) {
|
||||
fprintf(stderr, "Error opening or parsing file\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!parms.set(nm, value, sub)) {
|
||||
fprintf(stderr, "Set error\n");
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
} else if (op_flags & OPT_d) {
|
||||
ConfTree parms(filename, 0);
|
||||
if (parms.getStatus() != ConfSimple::STATUS_RW) {
|
||||
fprintf(stderr, "Error opening or parsing file\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!parms.erase(nm, sub)) {
|
||||
fprintf(stderr, "delete name '%s' in '%s' failed\n", nm, sub);
|
||||
exit(1);
|
||||
}
|
||||
printf("OK\n");
|
||||
exit(0);
|
||||
} else if (op_flags & OPT_s) {
|
||||
ConfSimple parms(filename, 1);
|
||||
if (parms.getStatus() == ConfSimple::STATUS_ERROR) {
|
||||
cerr << "Cant open /parse conf file " << filename << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
string source;
|
||||
if (!parms.get(string("strings"), source, "")) {
|
||||
cerr << "Cant get param 'strings'" << endl;
|
||||
exit(1);
|
||||
}
|
||||
cout << "source: [" << source << "]" << endl;
|
||||
list<string> strings;
|
||||
if (!stringToStrings(source, strings)) {
|
||||
cerr << "parse failed" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (list<string>::iterator it = strings.begin();
|
||||
it != strings.end(); it++) {
|
||||
cout << "[" << *it << "]" << endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
ConfTree parms(filename, 1);
|
||||
if (parms.getStatus() == ConfSimple::STATUS_ERROR) {
|
||||
fprintf(stderr, "Open failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("LIST\n");
|
||||
parms.listall();
|
||||
//printf("WALK\n");parms.sortwalk(mywalker, 0);
|
||||
printf("\nNAMES in global space:\n");
|
||||
list<string> names = parms.getNames("");
|
||||
for (list<string>::iterator it = names.begin();it!=names.end();
|
||||
it++)
|
||||
printf("%s\n", (*it).c_str());
|
||||
list<string> flist;
|
||||
while (argc--) {
|
||||
flist.push_back(*argv++);
|
||||
}
|
||||
bool ro = !(op_flags & (OPT_w|OPT_a|OPT_d));
|
||||
ConfNull *conf = 0;
|
||||
switch (flist.size()) {
|
||||
case 0:
|
||||
Usage();
|
||||
break;
|
||||
case 1:
|
||||
conf = new ConfTree(flist.front().c_str(), ro);
|
||||
break;
|
||||
default:
|
||||
conf = new ConfStack<ConfTree>(flist, ro);
|
||||
break;
|
||||
}
|
||||
|
||||
if (op_flags & OPT_w) {
|
||||
exit(!readwrite(conf));
|
||||
} else if (op_flags & OPT_q) {
|
||||
exit(!query(conf, nm, sub));
|
||||
} else if (op_flags & OPT_k) {
|
||||
if (!conf->ok()) {
|
||||
cerr << "conf init error" << endl;
|
||||
exit(1);
|
||||
}
|
||||
list<string>lst = conf->getSubKeys();
|
||||
for (list<string>::const_iterator it = lst.begin();
|
||||
it != lst.end(); it++) {
|
||||
cout << *it << endl;
|
||||
}
|
||||
exit(0);
|
||||
} else if (op_flags & OPT_a) {
|
||||
exit(!setvar(conf, nm, value, sub));
|
||||
} else if (op_flags & OPT_d) {
|
||||
exit(!erase(conf, nm, sub));
|
||||
} else if (op_flags & OPT_s) {
|
||||
if (!conf->ok()) {
|
||||
cerr << "Cant open /parse conf file " << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
string source;
|
||||
if (!conf->get(string("strings"), source, "")) {
|
||||
cerr << "Cant get param 'strings'" << endl;
|
||||
exit(1);
|
||||
}
|
||||
cout << "source: [" << source << "]" << endl;
|
||||
list<string> strings;
|
||||
if (!stringToStrings(source, strings)) {
|
||||
cerr << "parse failed" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (list<string>::iterator it = strings.begin();
|
||||
it != strings.end(); it++) {
|
||||
cout << "[" << *it << "]" << endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!conf->ok()) {
|
||||
fprintf(stderr, "Open failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("LIST\n");
|
||||
conf->listall();
|
||||
//printf("WALK\n");conf->sortwalk(mywalker, 0);
|
||||
printf("\nNAMES in global space:\n");
|
||||
list<string> names = conf->getNames("");
|
||||
for (list<string>::iterator it = names.begin();it!=names.end();
|
||||
it++)
|
||||
printf("%s\n", (*it).c_str());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -81,11 +81,28 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages a simple configuration file with subsections.
|
||||
* Virtual base class used to define an interface mostly useful for testing
|
||||
*/
|
||||
class ConfSimple {
|
||||
class ConfNull {
|
||||
public:
|
||||
enum StatusCode {STATUS_ERROR=0, STATUS_RO=1, STATUS_RW=2};
|
||||
virtual ~ConfNull() {};
|
||||
virtual int get(const string &name, string &value,
|
||||
const string &sk = "") = 0;
|
||||
virtual int set(const string &nm, const string &val,
|
||||
const string &sk = "") = 0;
|
||||
virtual bool ok() = 0;
|
||||
virtual list<string> getNames(const string &sk) = 0;
|
||||
virtual int erase(const string &name, const string &sk) {return 0;}
|
||||
virtual void listall() {}
|
||||
virtual list<string> getSubKeys() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages a simple configuration file with subsections.
|
||||
*/
|
||||
class ConfSimple : public ConfNull {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Build the object by reading content from file.
|
||||
@ -117,17 +134,14 @@ public:
|
||||
* global space if sk is empty).
|
||||
* @return 0 if name not found, 1 else
|
||||
*/
|
||||
virtual int get(const string &name, string &value,
|
||||
const string &sk = string(""));
|
||||
virtual int get(const string &name, string &value, const string &sk = "");
|
||||
/* Note: the version returning char* was buggy and has been removed */
|
||||
|
||||
/**
|
||||
* Set value for named parameter in specified subsection (or global)
|
||||
* @return 0 for error, 1 else
|
||||
*/
|
||||
virtual int set(const string &nm, const string &val,
|
||||
const string &sk);
|
||||
virtual int set(const char *name, const char *value, const char *sk = 0);
|
||||
virtual int set(const string &nm, const string &val, const string &sk = "");
|
||||
|
||||
/**
|
||||
* Remove name and value from config
|
||||
@ -167,7 +181,7 @@ public:
|
||||
* Copy constructor. Expensive but less so than a full rebuild
|
||||
*/
|
||||
ConfSimple(const ConfSimple &rhs)
|
||||
: m_data(0)
|
||||
: ConfNull(), m_data(0)
|
||||
{
|
||||
if ((status = rhs.status) == STATUS_ERROR)
|
||||
return;
|
||||
@ -250,10 +264,6 @@ public:
|
||||
* @return 0 if name not found, 1 else
|
||||
*/
|
||||
virtual int get(const string &name, string &value, const string &sk);
|
||||
|
||||
virtual int get(const char *name, string &value, const char *sk) {
|
||||
return get(string(name), value, sk ? string(sk) : string(""));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -262,17 +272,22 @@ public:
|
||||
* (ie personal) ones.
|
||||
*
|
||||
* Notes: it's ok for some of the files in the list to not exist, but the last
|
||||
* one must or we generate an error. We open all trees readonly.
|
||||
* one must or we generate an error. We open all trees readonly, except the
|
||||
* topmost one if requested. All writes go to the topmost file. Note that
|
||||
* erase() won't work.
|
||||
*/
|
||||
template <class T> class ConfStack {
|
||||
template <class T> class ConfStack : public ConfNull {
|
||||
public:
|
||||
/// Construct from list of configuration file names
|
||||
ConfStack(const list<string> &fns, bool ro = true) {
|
||||
/// Construct from list of configuration file names. The earler files in
|
||||
/// have priority when fetching values. Only the first file will be updated
|
||||
/// if ro is false and set() is used.
|
||||
ConfStack(const list<string> &fns, bool ro = true)
|
||||
{
|
||||
construct(fns, ro);
|
||||
}
|
||||
// Construct out of one name
|
||||
// Construct out of name and list of directories
|
||||
ConfStack(const string& nm, const list<string>& dirs, bool ro = true) {
|
||||
/// Construct out of single file name and list of directories
|
||||
ConfStack(const string& nm, const list<string>& dirs, bool ro = true)
|
||||
{
|
||||
list<string> fns;
|
||||
for (list<string>::const_iterator it = dirs.begin();
|
||||
it != dirs.end(); it++){
|
||||
@ -281,18 +296,22 @@ public:
|
||||
ConfStack::construct(fns, ro);
|
||||
}
|
||||
|
||||
~ConfStack() {
|
||||
erase();
|
||||
m_ok = false;
|
||||
}
|
||||
|
||||
ConfStack(const ConfStack &rhs) {
|
||||
ConfStack(const ConfStack &rhs)
|
||||
: ConfNull()
|
||||
{
|
||||
init_from(rhs);
|
||||
}
|
||||
|
||||
ConfStack& operator=(const ConfStack &rhs) {
|
||||
virtual ~ConfStack()
|
||||
{
|
||||
clear();
|
||||
m_ok = false;
|
||||
}
|
||||
|
||||
ConfStack& operator=(const ConfStack &rhs)
|
||||
{
|
||||
if (this != &rhs){
|
||||
erase();
|
||||
clear();
|
||||
m_ok = rhs.m_ok;
|
||||
if (m_ok)
|
||||
init_from(rhs);
|
||||
@ -300,7 +319,8 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
int get(const string &name, string &value, const string &sk) {
|
||||
virtual int get(const string &name, string &value, const string &sk)
|
||||
{
|
||||
typename list<T*>::iterator it;
|
||||
for (it = m_confs.begin();it != m_confs.end();it++) {
|
||||
if ((*it)->get(name, value, sk))
|
||||
@ -309,30 +329,49 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
int get(const char *name, string &value, const char *sk) {
|
||||
return get(string(name), value, sk ? string(sk) : string(""));
|
||||
virtual int set(const string &nm, const string &val, const string &sk = "")
|
||||
{
|
||||
if (!m_ok)
|
||||
return 0;
|
||||
return m_confs.front()->set(nm, val, sk);
|
||||
}
|
||||
|
||||
list<string> getNames(const string &sk) {
|
||||
virtual list<string> getNames(const string &sk)
|
||||
{
|
||||
list<string> nms;
|
||||
typename list<T*>::iterator it;
|
||||
for (it = m_confs.begin();it != m_confs.end(); it++) {
|
||||
list<string> lst;
|
||||
lst = (*it)->getNames(sk);
|
||||
nms.splice(nms.end(), lst);
|
||||
nms.insert(nms.end(), lst.begin(), lst.end());
|
||||
}
|
||||
nms.sort();
|
||||
nms.unique();
|
||||
return nms;
|
||||
}
|
||||
|
||||
bool ok() {return m_ok;}
|
||||
virtual list<string> getSubKeys()
|
||||
{
|
||||
list<string> sks;
|
||||
typename list<T*>::iterator it;
|
||||
for (it = m_confs.begin();it != m_confs.end(); it++) {
|
||||
list<string> lst;
|
||||
lst = (*it)->getSubKeys();
|
||||
sks.insert(sks.end(), lst.begin(), lst.end());
|
||||
}
|
||||
sks.sort();
|
||||
sks.unique();
|
||||
return sks;
|
||||
}
|
||||
|
||||
virtual bool ok() {return m_ok;}
|
||||
|
||||
private:
|
||||
bool m_ok;
|
||||
bool m_ok;
|
||||
list<T*> m_confs;
|
||||
|
||||
void erase() {
|
||||
|
||||
/// Reset to pristine
|
||||
void clear() {
|
||||
typename list<T*>::iterator it;
|
||||
for (it = m_confs.begin();it != m_confs.end();it++) {
|
||||
delete (*it);
|
||||
@ -350,21 +389,25 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/// Common constructor code
|
||||
/// Common construct from file list code
|
||||
void construct(const list<string> &fns, bool ro) {
|
||||
if (!ro) {
|
||||
m_ok = false;
|
||||
return;
|
||||
}
|
||||
list<string>::const_iterator it;
|
||||
bool lastok = false;
|
||||
for (it = fns.begin();it != fns.end();it++) {
|
||||
T* p = new T(it->c_str(), true);
|
||||
for (it = fns.begin(); it != fns.end(); it++) {
|
||||
T* p = new T(it->c_str(), ro);
|
||||
if (p && p->ok()) {
|
||||
m_confs.push_back(p);
|
||||
lastok = true;
|
||||
} else
|
||||
} else {
|
||||
delete p;
|
||||
lastok = false;
|
||||
if (!ro) {
|
||||
// For rw acccess, the topmost file needs to be ok
|
||||
// (ro is set to true after the first file)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ro = true;
|
||||
}
|
||||
m_ok = lastok;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user