From 569d96c43f1cff7db466d01e2cbd7078f92b5e7c Mon Sep 17 00:00:00 2001 From: dockes Date: Mon, 1 Oct 2007 06:19:21 +0000 Subject: [PATCH] config update enabling functions --- src/VERSION | 2 +- src/common/rclconfig.cpp | 4 +- src/common/rclconfig.h | 26 ++++- src/qtgui/confgui/confgui.cpp | 5 +- src/utils/conftree.cpp | 176 +++++++++++++++++++++++++--------- src/utils/conftree.h | 56 +++++++++-- 6 files changed, 211 insertions(+), 58 deletions(-) diff --git a/src/VERSION b/src/VERSION index 4abc0cc7..f5f6bc84 100644 --- a/src/VERSION +++ b/src/VERSION @@ -1 +1 @@ -1.9.1cjk +1.9.1cjk1 diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index 4622f33e..322ff4c0 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: rclconfig.cpp,v 1.49 2007-06-22 06:14:04 dockes Exp $ (C) 2004 J.F.Dockes"; +static char rcsid[] = "@(#$Id: rclconfig.cpp,v 1.50 2007-10-01 06:19:21 dockes Exp $ (C) 2004 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -107,7 +107,7 @@ RclConfig::RclConfig(const string *argcnf) cdirs.push_back(path_cat(m_datadir, "examples")); string cnferrloc = m_confdir + " or " + path_cat(m_datadir, "examples"); - m_conf = new ConfStack("recoll.conf", cdirs, true); + m_conf = new ConfStack("recoll.conf", cdirs, false); if (m_conf == 0 || !m_conf->ok()) { m_reason = string("No/bad main configuration file in: ") + cnferrloc; return; diff --git a/src/common/rclconfig.h b/src/common/rclconfig.h index 9bb5e85b..6c2770db 100644 --- a/src/common/rclconfig.h +++ b/src/common/rclconfig.h @@ -16,7 +16,7 @@ */ #ifndef _RCLCONFIG_H_INCLUDED_ #define _RCLCONFIG_H_INCLUDED_ -/* @(#$Id: rclconfig.h,v 1.36 2007-06-22 06:14:04 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: rclconfig.h,v 1.37 2007-10-01 06:19:21 dockes Exp $ (C) 2004 J.F.Dockes */ #include #include @@ -63,6 +63,8 @@ class RclConfig { guesscharset = stringToBool(str); } string getKeyDir() const {return m_keydir;} + /** Get all defined key directories in configuration */ + list getKeyDirs() {return m_conf->getSubKeys();} /** Get generic configuration parameter according to current keydir */ bool getConfParam(const string &name, string &value) @@ -71,6 +73,28 @@ class RclConfig { return false; return m_conf->get(name, value, m_keydir); } + /** Set generic configuration parameter according to current keydir */ + bool setConfParam(const string &name, const string &value) + { + if (m_conf == 0) + return false; + return m_conf->set(name, value, m_keydir); + } + /** Remove parameter from configuration */ + bool eraseConfParam(const string &name) + { + if (m_conf == 0) + return false; + return m_conf->erase(name, m_keydir); + } + /** Remove parameter from configuration */ + bool eraseKeyDir() + { + if (m_conf == 0) + return false; + return m_conf->eraseKey(m_keydir); + } + /** Variant with autoconversion to int */ bool getConfParam(const std::string &name, int *value); /** Variant with autoconversion to bool */ diff --git a/src/qtgui/confgui/confgui.cpp b/src/qtgui/confgui/confgui.cpp index f2e52ab8..53632779 100644 --- a/src/qtgui/confgui/confgui.cpp +++ b/src/qtgui/confgui/confgui.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: confgui.cpp,v 1.3 2007-09-29 09:06:53 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: confgui.cpp,v 1.4 2007-10-01 06:19:21 dockes Exp $ (C) 2005 J.F.Dockes"; #endif #include @@ -415,6 +415,9 @@ void ConfParamDNLW::showInputDialog() if (m_lb->findItem(s, Qt::ExactMatch) == 0) { m_lb->insertItem(s); m_lb->sort(); + QListBoxItem *item = m_lb->findItem(s, Qt::ExactMatch); + if (m_lb->selectionMode() == QListBox::Single && item) + m_lb->setSelected(item, true); listToConf(); } } diff --git a/src/utils/conftree.cpp b/src/utils/conftree.cpp index 7d364a7f..4c31dd89 100755 --- a/src/utils/conftree.cpp +++ b/src/utils/conftree.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid [] = "@(#$Id: conftree.cpp,v 1.11 2007-09-27 11:02:13 dockes Exp $ (C) 2003 J.F.Dockes"; +static char rcsid [] = "@(#$Id: conftree.cpp,v 1.12 2007-10-01 06:19:21 dockes Exp $ (C) 2003 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -44,6 +44,13 @@ using std::list; #define MIN(A,B) ((A)<(B) ? (A) : (B)) #endif +//#define DEBUG +#ifdef DEBUG +#define LOGDEB(X) fprintf X +#else +#define LOGDEB(X) +#endif + #define LL 1024 void ConfSimple::parseinput(istream &input) { @@ -54,7 +61,7 @@ void ConfSimple::parseinput(istream &input) for (;;) { input.getline(cline, LL-1); - // fprintf(stderr, "Line: '%s' status %d\n", cline, int(status)); + LOGDEB((stderr, "Parse:line: [%s] status %d\n", cline, int(status))); if (!input.good()) { if (input.bad()) { status = STATUS_ERROR; @@ -127,13 +134,13 @@ void ConfSimple::parseinput(istream &input) ConfSimple::ConfSimple(int readonly, bool tildexp) - : dotildexpand(tildexp), m_data(0) + : dotildexpand(tildexp), m_data(0), m_holdWrites(false) { status = readonly ? STATUS_RO : STATUS_RW; } ConfSimple::ConfSimple(string *d, int readonly, bool tildexp) - : dotildexpand(tildexp), m_data(d) + : dotildexpand(tildexp), m_data(d), m_holdWrites(false) { status = readonly ? STATUS_RO : STATUS_RW; @@ -142,7 +149,7 @@ ConfSimple::ConfSimple(string *d, int readonly, bool tildexp) } ConfSimple::ConfSimple(const char *fname, int readonly, bool tildexp) - : dotildexpand(tildexp), m_filename(fname), m_data(0) + : dotildexpand(tildexp), m_filename(fname), m_data(0), m_holdWrites(false) { status = readonly ? STATUS_RO : STATUS_RW; @@ -204,7 +211,7 @@ int ConfSimple::get(const string &nm, string &value, const string &sk) return 1; } -// Code to appropriately output a subkey (nm=="") or variable line +// Appropriately output a subkey (nm=="") or variable line. // Splits long lines static ConfSimple::WalkerCode varprinter(void *f, const string &nm, const string &value) @@ -249,22 +256,34 @@ int ConfSimple::set(const std::string &nm, const std::string &value, int ConfSimple::i_set(const std::string &nm, const std::string &value, const string &sk, bool init) { + LOGDEB((stderr, "ConfSimple::i_set: nm[%s] val[%s] key[%s], init %d\n", + nm.c_str(), value.c_str(), sk.c_str(), init)); // Values must not have embedded newlines if (value.find_first_of("\n\r") != string::npos) { + LOGDEB((stderr, "ConfSimple::i_set: LF in value\n")); return 0; } bool existing = false; map >::iterator ss; + // Test if submap already exists, else create it, and insert variable: if ((ss = m_submaps.find(sk)) == m_submaps.end()) { + LOGDEB((stderr, "ConfSimple::i_set: new submap\n")); map submap; submap[nm] = value; m_submaps[sk] = submap; - if (!sk.empty()) - m_order.push_back(ConfLine(ConfLine::CFL_SK, sk)); - // The var insert will be at the end, need not search for the - // right place - init = true; + + // Maybe add sk entry to m_order data: + if (!sk.empty()) { + ConfLine nl(ConfLine::CFL_SK, sk); + // Append SK entry only if it's not already there (erase + // does not remove entries from the order data, adn it may + // be being recreated after deletion) + if (find(m_order.begin(), m_order.end(), nl) == m_order.end()) { + m_order.push_back(nl); + } + } } else { + // Insert or update variable in existing map. map::iterator it; it = ss->second.find(nm); if (it == ss->second.end()) { @@ -275,21 +294,29 @@ int ConfSimple::i_set(const std::string &nm, const std::string &value, } } - // If the variable already existed, no need to change the order data - if (existing) + // If the variable already existed, no need to change the m_order data + if (existing) { + LOGDEB((stderr, "ConfSimple::i_set: existing var: no order update\n")); return 1; + } // Add the new variable at the end of its submap in the order data. if (init) { - // During the initial construction, insert at end + // During the initial construction, just append: + LOGDEB((stderr, "ConfSimple::i_set: init true: append\n")); m_order.push_back(ConfLine(ConfLine::CFL_VAR, nm)); return 1; } + // Look for the start and end of the subkey zone. Start is either + // at begin() for a null subkey, or just behind the subkey + // entry. End is either the next subkey entry, or the end of + // list. We insert the new entry just before end. list::iterator start, fin; if (sk.empty()) { start = m_order.begin(); + LOGDEB((stderr,"ConfSimple::i_set: null sk, start at top of order\n")); } else { start = find(m_order.begin(), m_order.end(), ConfLine(ConfLine::CFL_SK, sk)); @@ -304,7 +331,9 @@ int ConfSimple::i_set(const std::string &nm, const std::string &value, fin = m_order.end(); if (start != m_order.end()) { - start++; + // The null subkey has no entry (maybe it should) + if (!sk.empty()) + start++; for (list::iterator it = start; it != m_order.end(); it++) { if (it->m_kind == ConfLine::CFL_SK) { fin = it; @@ -314,10 +343,10 @@ int ConfSimple::i_set(const std::string &nm, const std::string &value, } // It may happen that the order entry already exists because erase doesnt - // update m_order (fix it ?) - if (find(start, fin, ConfLine(ConfLine::CFL_VAR, nm)) == fin) + // update m_order + if (find(start, fin, ConfLine(ConfLine::CFL_VAR, nm)) == fin) { m_order.insert(fin, ConfLine(ConfLine::CFL_VAR, nm)); - + } return 1; } @@ -332,10 +361,22 @@ int ConfSimple::erase(const string &nm, const string &sk) } ss->second.erase(nm); - + if (ss->second.empty()) { + m_submaps.erase(ss); + } return write(); } +int ConfSimple::eraseKey(const string &sk) +{ + listnms = getNames(sk); + for (list::iterator it = nms.begin(); it != nms.end(); it++) { + erase(*it, sk); + } + return write(); +} + +// Walk the tree, calling user function at each node ConfSimple::WalkerCode ConfSimple::sortwalk(WalkerCode (*walker)(void *,const string&,const string&), void *clidata) @@ -362,8 +403,13 @@ ConfSimple::sortwalk(WalkerCode (*walker)(void *,const string&,const string&), return WALK_CONTINUE; } +// Write to default output: bool ConfSimple::write() { + if (!ok()) + return false; + if (m_holdWrites) + return true; if (m_filename.length()) { ofstream output(m_filename.c_str(), ios::out|ios::trunc); if (!output.is_open()) @@ -378,6 +424,9 @@ bool ConfSimple::write() } } +// Write out the tree in configuration file format: +// This does not check holdWrites, this is done by write(void), which +// lets ie: listall work even when holdWrites is set bool ConfSimple::write(ostream& out) { if (!ok()) @@ -393,19 +442,34 @@ bool ConfSimple::write(ostream& out) break; case ConfLine::CFL_SK: sk = it->m_data; - out << "[" << it->m_data << "]" << endl; - if (!out.good()) - return false; - break; - case ConfLine::CFL_VAR: - string value; - // As erase() doesnt update m_order we can find unexisting - // variables, and must not output anything for them - if (get(it->m_data, value, sk)) { - varprinter(&out, it->m_data, value); + LOGDEB((stderr, "ConfSimple::write: SK [%s]\n", sk.c_str())); + // Check that the submap still exists, and only output it if it + // does + if (m_submaps.find(sk) != m_submaps.end()) { + out << "[" << it->m_data << "]" << endl; if (!out.good()) return false; } + break; + case ConfLine::CFL_VAR: + string nm = it->m_data; + LOGDEB((stderr, "ConfSimple::write: VAR [%s], sk [%s]\n", + nm.c_str(), sk.c_str())); + // As erase() doesnt update m_order we can find unexisting + // variables, and must not output anything for them. Have + // to use a ConfSimple::get() to check here, because + // ConfTree's could retrieve from an ancestor even if the + // local var is gone. + string value; + if (ConfSimple::get(nm, value, sk)) { + varprinter(&out, nm, value); + if (!out.good()) + return false; + break; + } + LOGDEB((stderr, "ConfSimple::write: no value: nm[%s] sk[%s]\n", + nm.c_str(), sk.c_str())); + break; } } return true; @@ -455,7 +519,7 @@ list ConfSimple::getSubKeys() int ConfTree::get(const std::string &name, string &value, const string &sk) { if (sk.empty() || sk[0] != '/') { - // fprintf(stderr, "Looking in global space"); + // LOGDEB((stderr, "ConfTree::get: looking in global space\n")); return ConfSimple::get(name, value, sk); } @@ -468,8 +532,8 @@ int ConfTree::get(const std::string &name, string &value, const string &sk) // Look in subkey and up its parents until root ('') for (;;) { - //fprintf(stderr,"Looking for '%s' in '%s'\n", - //name.c_str(), msk.c_str()); + // LOGDEB((stderr,"ConfTree::get: looking for '%s' in '%s'\n", + // name.c_str(), msk.c_str())); if (ConfSimple::get(name, value, msk)) return 1; string::size_type pos = msk.rfind("/"); @@ -600,6 +664,20 @@ bool erase(ConfNull *conf, const string& nm, const string& sub) return true; } +bool eraseKey(ConfNull *conf, const string& sub) +{ + if (!conf->ok()) { + cerr << "Error opening or parsing file\n" << endl; + return false; + } + + if (!conf->eraseKey(sub)) { + cerr << "delete key [" << sub << "] failed" << endl; + return false; + } + return true; +} + bool setvar(ConfNull *conf, const string& nm, const string& value, const string& sub) { @@ -618,9 +696,10 @@ 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/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" + "-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" + "-E sect : erase key (and all its names)\n" "[-S] : string io test. No filename in this case\n" "[-V] : volatile config test. No filename in this case\n" ; @@ -639,6 +718,7 @@ static int op_flags; #define OPT_V 0x40 #define OPT_a 0x80 #define OPT_k 0x100 +#define OPT_E 0x200 int main(int argc, char **argv) { @@ -656,13 +736,6 @@ int main(int argc, char **argv) Usage(); while (**argv) switch (*(*argv)++) { - case 'd': - op_flags |= OPT_d; - if (argc < 3) - Usage(); - nm = *(++argv);argc--; - sub = *(++argv);argc--; - goto b1; case 'a': op_flags |= OPT_a; if (argc < 4) @@ -671,6 +744,20 @@ int main(int argc, char **argv) value = *(++argv);argc--; sub = *(++argv);argc--; goto b1; + case 'd': + op_flags |= OPT_d; + if (argc < 3) + Usage(); + nm = *(++argv);argc--; + sub = *(++argv);argc--; + goto b1; + case 'E': + op_flags |= OPT_E; + if (argc < 2) + Usage(); + sub = *(++argv);argc--; + goto b1; + case 'k': op_flags |= OPT_k; break; case 'q': op_flags |= OPT_q; if (argc < 3) @@ -679,7 +766,6 @@ 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; @@ -714,7 +800,7 @@ int main(int argc, char **argv) while (argc--) { flist.push_back(*argv++); } - bool ro = !(op_flags & (OPT_w|OPT_a|OPT_d)); + bool ro = !(op_flags & (OPT_w|OPT_a|OPT_d|OPT_E)); ConfNull *conf = 0; switch (flist.size()) { case 0: @@ -747,6 +833,8 @@ int main(int argc, char **argv) exit(!setvar(conf, nm, value, sub)); } else if (op_flags & OPT_d) { exit(!erase(conf, nm, sub)); + } else if (op_flags & OPT_E) { + exit(!eraseKey(conf, sub)); } else if (op_flags & OPT_s) { if (!conf->ok()) { cerr << "Cant open /parse conf file " << endl; diff --git a/src/utils/conftree.h b/src/utils/conftree.h index 0626e790..66860024 100755 --- a/src/utils/conftree.h +++ b/src/utils/conftree.h @@ -85,7 +85,6 @@ public: { return o.m_kind == m_kind && o.m_data == m_data; } - }; /** @@ -102,8 +101,10 @@ public: virtual bool ok() = 0; virtual list getNames(const string &sk) = 0; virtual int erase(const string &, const string &) {return 0;} + virtual int eraseKey(const string &) {return 0;} virtual void listall() {} virtual list getSubKeys() = 0; + virtual bool holdWrites(bool) {return true;} }; /** @@ -137,13 +138,24 @@ public: virtual ~ConfSimple() {}; + /** + * Decide if we actually rewrite the backing-store after modifying the + * tree. (Re-)Enabling writes causes a flush. + */ + virtual bool holdWrites(bool on) + { + m_holdWrites = on; + if (m_holdWrites == false) + return write(); + return true; + } + /** * Get value for named parameter, from specified subsection (looks in * 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 = ""); - /* Note: the version returning char* was buggy and has been removed */ /** * Set value for named parameter in specified subsection (or global) @@ -156,6 +168,11 @@ public: */ virtual int erase(const string &name, const string &sk); + /** + * Erase all names under given subkey (and subkey itself) + */ + virtual int eraseKey(const string &sk); + virtual StatusCode getStatus(); virtual bool ok() {return getStatus() != STATUS_ERROR;} @@ -171,6 +188,8 @@ public: (*wlkr)(void *cldata, const string &nm, const string &val), void *clidata); + + /** List all values to stdout */ virtual void listall(); /** @@ -228,6 +247,8 @@ private: // variable and subkey ordering information in there (for // rewriting the file while keeping hand-edited information) list m_order; + // Control if we're writing to the backing store + bool m_holdWrites; void parseinput(istream& input); bool write(); @@ -254,11 +275,14 @@ private: class ConfTree : public ConfSimple { public: - /** - * Build the object by reading content from file. - */ + /* The constructors just call ConfSimple's, asking for key tilde + * expansion */ ConfTree(const char *fname, int readonly = 0) : ConfSimple(fname, readonly, true) {} + ConfTree(string *data, int readonly = 0) + : ConfSimple(data, readonly, true) {} + ConfTree(int readonly = 0) + : ConfSimple(readonly, true) {} virtual ~ConfTree() {}; ConfTree(const ConfTree& r) : ConfSimple(r) {}; ConfTree& operator=(const ConfTree& r) { @@ -282,13 +306,14 @@ public: * 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, except the * topmost one if requested. All writes go to the topmost file. Note that - * erase() won't work. + * erase() won't work except for parameters only defined in the topmost + * file (it erases only from there). */ template class ConfStack : public ConfNull { public: - /// 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. + /// 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 &fns, bool ro = true) { construct(fns, ro); @@ -366,6 +391,19 @@ public: return m_confs.front()->set(nm, val, sk); } + virtual int erase(const string &nm, const string &sk) + { + return m_confs.front()->erase(nm, sk); + } + virtual int eraseKey(const string &sk) + { + return m_confs.front()->eraseKey(sk); + } + virtual bool holdWrites(bool on) + { + return m_confs.front()->holdWrites(on); + } + virtual list getNames(const string &sk) { list nms;