Switch to side-by-side view

--- 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 @@
 #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 @@
 
     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 @@
 
 
 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(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 @@
     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::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<string, map<string, string> >::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<string, string> 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<string, string>::iterator it;
 	it = ss->second.find(nm);
 	if (it == ss->second.end()) {
@@ -275,21 +294,29 @@
 	}
     }
 
-    // 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<ConfLine>::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 @@
 
     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<ConfLine>::iterator it = start; it != m_order.end(); it++) {
 	    if (it->m_kind == ConfLine::CFL_SK) {
 		fin = it;
@@ -314,10 +343,10 @@
     }
 
     // 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 @@
     }
     
     ss->second.erase(nm);
-  
+    if (ss->second.empty()) {
+	m_submaps.erase(ss);
+    }
     return write();
 }
 
+int ConfSimple::eraseKey(const string &sk)
+{
+    list<string>nms = getNames(sk);
+    for (list<string>::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 @@
     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 @@
     }
 }
 
+// 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 @@
 	    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 @@
 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 @@
 
     // 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 @@
     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 @@
     "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 @@
 #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 @@
 	    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 @@
 		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 @@
 		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 @@
     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 @@
 	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;