Allow updates in confstacks

This commit is contained in:
dockes 2007-08-04 07:22:43 +00:00
parent 3ef569c5db
commit 96aff5eb29
2 changed files with 249 additions and 163 deletions

View File

@ -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());
}
}

View File

@ -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;
}