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 #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 #endif
/* /*
* This program is free software; you can redistribute it and/or modify * 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(); 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::WalkerCode
ConfSimple::sortwalk(WalkerCode (*walker)(void *,const string&,const string&), ConfSimple::sortwalk(WalkerCode (*walker)(void *,const string&,const string&),
void *clidata) void *clidata)
@ -538,11 +532,89 @@ void memtest(ConfSimple &c)
c.listall(); 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 [] = static char usage [] =
"testconftree [opts] filename\n" "testconftree [opts] filename\n"
"[-w] : read/write test.\n" "[-w] : read/write test.\n"
"[-s] : string parsing test. Filename must hold parm 'strings'\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" "[-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" "[-d] nm sect : delete nm in 'sect' which can be ''\n"
"[-S] : string io test. No filename in this case\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_d 0x20
#define OPT_V 0x40 #define OPT_V 0x40
#define OPT_a 0x80 #define OPT_a 0x80
#define OPT_k 0x100
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -602,6 +675,7 @@ int main(int argc, char **argv)
sub = *(++argv);argc--; sub = *(++argv);argc--;
goto b1; goto b1;
case 's': op_flags |= OPT_s; break; case 's': op_flags |= OPT_s; break;
case 'k': op_flags |= OPT_k; break;
case 'S': op_flags |= OPT_S; break; case 'S': op_flags |= OPT_S; break;
case 'V': op_flags |= OPT_S; break; case 'V': op_flags |= OPT_S; break;
case 'w': op_flags |= OPT_w; break; case 'w': op_flags |= OPT_w; break;
@ -612,131 +686,100 @@ int main(int argc, char **argv)
} }
if ((op_flags & OPT_S)) { if ((op_flags & OPT_S)) {
// String storage test
if (argc != 0) if (argc != 0)
Usage(); Usage();
string s; string s;
ConfSimple c(&s); ConfSimple c(&s);
memtest(c); memtest(c);
exit(0);
} else if ((op_flags & OPT_V)) { } else if ((op_flags & OPT_V)) {
// No storage test
if (argc != 0) if (argc != 0)
Usage(); Usage();
string s;
ConfSimple c; ConfSimple c;
memtest(c); memtest(c);
} else { exit(0);
if (argc < 1) }
Usage();
const char *filename = *argv++;argc--; // Other tests use file(s) as backing store
if (argc < 1)
Usage();
if (op_flags & OPT_w) { list<string> flist;
ConfSimple parms(filename); while (argc--) {
if (parms.getStatus() != ConfSimple::STATUS_ERROR) { flist.push_back(*argv++);
// It's ok for the file to not exist here }
string value; bool ro = !(op_flags & (OPT_w|OPT_a|OPT_d));
ConfNull *conf = 0;
if (parms.get("mypid", value)) { switch (flist.size()) {
printf("Value for mypid is '%s'\n", value.c_str()); case 0:
} else { Usage();
printf("mypid not set\n"); break;
} case 1:
conf = new ConfTree(flist.front().c_str(), ro);
if (parms.get("unstring", value)) { break;
printf("Value for unstring is '%s'\n", value.c_str()); default:
} else { conf = new ConfStack<ConfTree>(flist, ro);
printf("unstring not set\n"); break;
} }
}
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());
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: public:
enum StatusCode {STATUS_ERROR=0, STATUS_RO=1, STATUS_RW=2}; 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. * Build the object by reading content from file.
@ -117,17 +134,14 @@ public:
* global space if sk is empty). * global space if sk is empty).
* @return 0 if name not found, 1 else * @return 0 if name not found, 1 else
*/ */
virtual int get(const string &name, string &value, virtual int get(const string &name, string &value, const string &sk = "");
const string &sk = string(""));
/* Note: the version returning char* was buggy and has been removed */ /* Note: the version returning char* was buggy and has been removed */
/** /**
* Set value for named parameter in specified subsection (or global) * Set value for named parameter in specified subsection (or global)
* @return 0 for error, 1 else * @return 0 for error, 1 else
*/ */
virtual int set(const string &nm, const string &val, virtual int set(const string &nm, const string &val, const string &sk = "");
const string &sk);
virtual int set(const char *name, const char *value, const char *sk = 0);
/** /**
* Remove name and value from config * Remove name and value from config
@ -167,7 +181,7 @@ public:
* Copy constructor. Expensive but less so than a full rebuild * Copy constructor. Expensive but less so than a full rebuild
*/ */
ConfSimple(const ConfSimple &rhs) ConfSimple(const ConfSimple &rhs)
: m_data(0) : ConfNull(), m_data(0)
{ {
if ((status = rhs.status) == STATUS_ERROR) if ((status = rhs.status) == STATUS_ERROR)
return; return;
@ -250,10 +264,6 @@ public:
* @return 0 if name not found, 1 else * @return 0 if name not found, 1 else
*/ */
virtual int get(const string &name, string &value, const string &sk); 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. * (ie personal) ones.
* *
* Notes: it's ok for some of the files in the list to not exist, but the last * 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: public:
/// Construct from list of configuration file names /// Construct from list of configuration file names. The earler files in
ConfStack(const list<string> &fns, bool ro = true) { /// 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(fns, ro);
} }
// Construct out of one name /// Construct out of single file name and list of directories
// Construct out of name and list of directories ConfStack(const string& nm, const list<string>& dirs, bool ro = true)
ConfStack(const string& nm, const list<string>& dirs, bool ro = true) { {
list<string> fns; list<string> fns;
for (list<string>::const_iterator it = dirs.begin(); for (list<string>::const_iterator it = dirs.begin();
it != dirs.end(); it++){ it != dirs.end(); it++){
@ -281,18 +296,22 @@ public:
ConfStack::construct(fns, ro); ConfStack::construct(fns, ro);
} }
~ConfStack() { ConfStack(const ConfStack &rhs)
erase(); : ConfNull()
m_ok = false; {
}
ConfStack(const ConfStack &rhs) {
init_from(rhs); init_from(rhs);
} }
ConfStack& operator=(const ConfStack &rhs) { virtual ~ConfStack()
{
clear();
m_ok = false;
}
ConfStack& operator=(const ConfStack &rhs)
{
if (this != &rhs){ if (this != &rhs){
erase(); clear();
m_ok = rhs.m_ok; m_ok = rhs.m_ok;
if (m_ok) if (m_ok)
init_from(rhs); init_from(rhs);
@ -300,7 +319,8 @@ public:
return *this; 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; typename list<T*>::iterator it;
for (it = m_confs.begin();it != m_confs.end();it++) { for (it = m_confs.begin();it != m_confs.end();it++) {
if ((*it)->get(name, value, sk)) if ((*it)->get(name, value, sk))
@ -309,30 +329,49 @@ public:
return false; return false;
} }
int get(const char *name, string &value, const char *sk) { virtual int set(const string &nm, const string &val, const string &sk = "")
return get(string(name), value, sk ? string(sk) : string("")); {
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; list<string> nms;
typename list<T*>::iterator it; typename list<T*>::iterator it;
for (it = m_confs.begin();it != m_confs.end(); it++) { for (it = m_confs.begin();it != m_confs.end(); it++) {
list<string> lst; list<string> lst;
lst = (*it)->getNames(sk); lst = (*it)->getNames(sk);
nms.splice(nms.end(), lst); nms.insert(nms.end(), lst.begin(), lst.end());
} }
nms.sort(); nms.sort();
nms.unique(); nms.unique();
return nms; 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: private:
bool m_ok; bool m_ok;
list<T*> m_confs; list<T*> m_confs;
void erase() { /// Reset to pristine
void clear() {
typename list<T*>::iterator it; typename list<T*>::iterator it;
for (it = m_confs.begin();it != m_confs.end();it++) { for (it = m_confs.begin();it != m_confs.end();it++) {
delete (*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) { void construct(const list<string> &fns, bool ro) {
if (!ro) {
m_ok = false;
return;
}
list<string>::const_iterator it; list<string>::const_iterator it;
bool lastok = false; bool lastok = false;
for (it = fns.begin();it != fns.end();it++) { for (it = fns.begin(); it != fns.end(); it++) {
T* p = new T(it->c_str(), true); T* p = new T(it->c_str(), ro);
if (p && p->ok()) { if (p && p->ok()) {
m_confs.push_back(p); m_confs.push_back(p);
lastok = true; lastok = true;
} else } else {
delete p;
lastok = false; 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; m_ok = lastok;
} }