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.8 2006-12-14 13:53:43 dockes Exp $  (C) 2003 J.F.Dockes";
+static char rcsid [] = "@(#$Id: conftree.cpp,v 1.9 2007-08-03 07:50:49 dockes Exp $  (C) 2003 J.F.Dockes";
 #endif
 /*
  *   This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
 #include <fstream>
 #include <sstream>
 #include <algorithm>
+#include <iostream>
 
 #include "conftree.h"
 #include "pathut.h"
@@ -42,7 +43,6 @@
 #ifndef MIN
 #define MIN(A,B) ((A)<(B) ? (A) : (B))
 #endif
-
 
 #define LL 1024
 void ConfSimple::parseinput(istream &input)
@@ -78,8 +78,10 @@
 	// Note that we trim whitespace before checking for backslash-eol
 	// This avoids invisible problems.
 	trimstring(line);
-	if (line.empty())
+	if (line.empty()) {
+	    m_order.push_back(ConfLine(ConfLine::CFL_COMMENT, line));
 	    continue;
+	}
 	if (line[line.length() - 1] == '\\') {
 	    line.erase(line.length() - 1);
 	    appending = true;
@@ -93,13 +95,20 @@
 		submapkey = path_tildexpand(line);
 	    else 
 		submapkey = line;
+	    // No need for adding sk to order, will be done with first
+	    // variable insert. Also means that empty section are
+	    // expandable (won't be output when rewriting)
+	    // Another option would be to add the subsec to m_order here
+	    // and not do it inside i_set() if init is true
 	    continue;
 	}
 
 	// Look for first equal sign
 	string::size_type eqpos = line.find("=");
-	if (eqpos == string::npos)
+	if (eqpos == string::npos) {
+	    m_order.push_back(ConfLine(ConfLine::CFL_COMMENT, line));
 	    continue;
+	}
 
 	// Compute name and value, trim white space
 	string nm, val;
@@ -108,48 +117,33 @@
 	val = line.substr(eqpos+1, string::npos);
 	trimstring(val);
 	
-	if (nm.length() == 0)
+	if (nm.length() == 0) {
+	    m_order.push_back(ConfLine(ConfLine::CFL_COMMENT, line));
 	    continue;
-
-	map<string, map<string, string> >::iterator s;
-	s = submaps.find(submapkey);
-	if (s != submaps.end()) {
-	    // submap already exists
-	    map<string, string> &sm = s->second;
-	    sm[nm] = val;
-	} else {
-	    map<string, string> newmap;
-	    newmap[nm] = val;
-	    submaps[submapkey] = newmap;
-	}
-
+	}
+	i_set(nm, val, submapkey, true);
     }
 }
 
 
 ConfSimple::ConfSimple(int readonly, bool tildexp)
-{
-    data = 0;
-    dotildexpand = tildexp;
+    : dotildexpand(tildexp), m_data(0)
+{
     status = readonly ? STATUS_RO : STATUS_RW;
 }
 
 ConfSimple::ConfSimple(string *d, int readonly, bool tildexp)
-{
-    data = d;
-    dotildexpand = tildexp;
+    : dotildexpand(tildexp), m_data(d)
+{
     status = readonly ? STATUS_RO : STATUS_RW;
 
     stringstream input(*d, ios::in);
     parseinput(input);
 }
 
-
 ConfSimple::ConfSimple(const char *fname, int readonly, bool tildexp)
-{
-    data = 0;
-    filename = string(fname);
-    dotildexpand = tildexp;
+    : dotildexpand(tildexp), m_filename(fname), m_data(0)
+{
     status = readonly ? STATUS_RO : STATUS_RW;
 
     ifstream input;
@@ -180,7 +174,6 @@
 	return;
     }	    
 
-    // Parse
     parseinput(input);
 }
 
@@ -200,7 +193,7 @@
 
     // Find submap
     map<string, map<string, string> >::iterator ss;
-    if ((ss = submaps.find(sk)) == submaps.end()) 
+    if ((ss = m_submaps.find(sk)) == m_submaps.end()) 
 	return 0;
 
     // Find named value
@@ -211,13 +204,15 @@
     return 1;
 }
 
-static ConfSimple::WalkerCode swalker(void *f, const string &nm, 
+// Code to appropriately output a subkey (nm=="") or variable line
+// Splits long lines
+static ConfSimple::WalkerCode varprinter(void *f, const string &nm, 
 				      const string &value)
 {
     ostream *output = (ostream *)f;
-    if (nm.empty())
+    if (nm.empty()) {
 	*output << "\n[" << value << "]\n";
-    else {
+    } else {
 	string value1;
 	if (value.length() < 60) {
 	    value1 = value;
@@ -236,46 +231,90 @@
     return ConfSimple::WALK_CONTINUE;
 }
 
+// Set variable and rewrite data
 int ConfSimple::set(const std::string &nm, const std::string &value, 
-		  const string &sk)
+		    const string &sk)
 {
     if (status  != STATUS_RW)
 	return 0;
 
-    // Preprocess value: we don't want nl's in there, and we want to keep
-    // lines to a reasonable length
+    if (!i_set(nm, value, sk))
+	return 0;
+    return write();
+}
+
+// Internal set variable: no rw checking or file rewriting. If init is
+// set, we're doing initial parsing, else we are changing a parsed
+// tree (changes the way we update the order data)
+int ConfSimple::i_set(const std::string &nm, const std::string &value, 
+		      const string &sk, bool init)
+{
+    // Values must not have embedded newlines
     if (value.find_first_of("\n\r") != string::npos) {
 	return 0;
     }
-
+    bool existing = false;
     map<string, map<string, string> >::iterator ss;
-    if ((ss = submaps.find(sk)) == submaps.end()) {
+    if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
 	map<string, string> submap;
 	submap[nm] = value;
-	submaps[sk] = submap;
-
+	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;
     } else {
-	ss->second[nm] = value;
-    }
-  
-    if (filename.length()) {
-	ofstream output(filename.c_str(), ios::out|ios::trunc);
-	if (!output.is_open())
-	    return 0;
-	if (sortwalk(swalker, &output) != WALK_CONTINUE) {
-	    return 0;
-	}
+	map<string, string>::iterator it;
+	it = ss->second.find(nm);
+	if (it == ss->second.end()) {
+	    ss->second.insert(pair<string,string>(nm, value));
+	} else {
+	    it->second = value;
+	    existing = true;
+	}
+    }
+
+    // If the variable already existed, no need to change the order data
+    if (existing)
 	return 1;
-    } else if (data) {
-	ostringstream output(*data, ios::out | ios::trunc);
-	if (sortwalk(swalker, &output) != WALK_CONTINUE) {
-	    return 0;
-	}
+
+    // Add the new variable at the end of its submap in the order data.
+
+    if (init) {
+	// During the initial construction, insert at end
+	m_order.push_back(ConfLine(ConfLine::CFL_VAR, nm));
 	return 1;
+    } 
+
+    list<ConfLine>::iterator start, fin;
+    if (sk.empty()) {
+	start = m_order.begin();
     } else {
-	// No backing store, no writing
-	return 1;
-    }
+	start = find(m_order.begin(), m_order.end(), 
+		     ConfLine(ConfLine::CFL_SK, sk));
+	if (start == m_order.end()) {
+	    // This is not logically possible. The subkey must
+	    // exist. We're doomed
+	    std::cerr << "Logical failure during configuration variable " 
+		"insertion" << endl;
+	    abort();
+	}
+    }
+
+    fin = m_order.end();
+    if (start != m_order.end()) {
+	start++;
+	for (list<ConfLine>::iterator it = start; it != m_order.end(); it++) {
+	    if (it->m_kind == ConfLine::CFL_SK) {
+		fin = it;
+		break;
+	    }
+	}
+    }
+    m_order.insert(fin, ConfLine(ConfLine::CFL_VAR, nm));
+
+    return 1;
 }
 
 int ConfSimple::erase(const string &nm, const string &sk)
@@ -284,30 +323,13 @@
 	return 0;
 
     map<string, map<string, string> >::iterator ss;
-    if ((ss = submaps.find(sk)) == submaps.end()) {
+    if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
 	return 0;
-
     }
     
     ss->second.erase(nm);
   
-    if (filename.length()) {
-	ofstream output(filename.c_str(), ios::out|ios::trunc);
-	if (!output.is_open())
-	    return 0;
-	if (sortwalk(swalker, &output) != WALK_CONTINUE) {
-	    return 0;
-	}
-	return 1;
-    } else if (data) {
-	ostringstream output(*data, ios::out | ios::trunc);
-	if (sortwalk(swalker, &output) != WALK_CONTINUE) {
-	    return 0;
-	}
-	return 1;
-    } else {
-	return 1;
-    }
+    return write();
 }
 
 int ConfSimple::set(const char *nm, const char *value, const char *sk)
@@ -323,8 +345,8 @@
     if (!ok())
 	return WALK_STOP;
     // For all submaps:
-    for (map<string, map<string, string> >::iterator sit = submaps.begin();
-	 sit != submaps.end(); sit++) {
+    for (map<string, map<string, string> >::iterator sit = m_submaps.begin();
+	 sit != m_submaps.end(); sit++) {
 
 	// Possibly emit submap name:
 	if (!sit->first.empty() && walker(clidata, "", sit->first.c_str())
@@ -342,12 +364,60 @@
     return WALK_CONTINUE;
 }
 
-#include <iostream>
+bool ConfSimple::write()
+{
+    if (m_filename.length()) {
+	ofstream output(m_filename.c_str(), ios::out|ios::trunc);
+	if (!output.is_open())
+	    return 0;
+	return write(output);
+    } else if (m_data) {
+	ostringstream output(*m_data, ios::out | ios::trunc);
+	return write(output);
+    } else {
+	// No backing store, no writing
+	return 1;
+    }
+}
+
+bool ConfSimple::write(ostream& out)
+{
+    if (!ok())
+	return false;
+    string sk;
+    for (list<ConfLine>::const_iterator it = m_order.begin(); 
+	 it != m_order.end(); it++) {
+	switch(it->m_kind) {
+	case ConfLine::CFL_COMMENT: 
+	    out << it->m_data << endl; 
+	    if (!out.good()) 
+		return false;
+	    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);
+		if (!out.good()) 
+		    return false;
+	    }
+	}
+    }
+    return true;
+}
+
 void ConfSimple::listall()
 {
     if (!ok())
 	return;
-    sortwalk(swalker, &std::cout);
+    write(std::cout);
 }
 
 list<string> ConfSimple::getNames(const string &sk)
@@ -356,7 +426,7 @@
     if (!ok())
 	return mylist;
     map<string, map<string, string> >::iterator ss;
-    if ((ss = submaps.find(sk)) == submaps.end()) {
+    if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
 	return mylist;
     }
     map<string, string>::const_iterator it;
@@ -365,6 +435,18 @@
     }
     mylist.sort();
     mylist.unique();
+    return mylist;
+}
+
+list<string> ConfSimple::getSubKeys()
+{
+    std::list<string> mylist;
+    if (!ok())
+	return mylist;
+    map<string, map<string, string> >::iterator ss;
+    for (ss = m_submaps.begin(); ss != m_submaps.end(); ss++) {
+	mylist.push_back(ss->first);
+    }
     return mylist;
 }
 
@@ -460,6 +542,7 @@
     "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"
     "[-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"
@@ -478,11 +561,13 @@
 #define OPT_S     0x10
 #define OPT_d     0x20
 #define OPT_V     0x40
+#define OPT_a     0x80
 
 int main(int argc, char **argv)
 {
     const char *nm = 0;
     const char *sub = 0;
+    const char *value = 0;
 
     thisprog = argv[0];
     argc--; argv++;
@@ -494,11 +579,19 @@
 	    Usage();
 	while (**argv)
 	    switch (*(*argv)++) {
-	    case 'd':   
+	    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)  
+		    Usage();
+		nm = *(++argv);argc--;
+		value = *(++argv);argc--;
 		sub = *(++argv);argc--;		  
 		goto b1;
 	    case 'q':
@@ -556,13 +649,18 @@
 	    }
 	    char spid[100];
 	    sprintf(spid, "%d", getpid());
-	    parms.set("mypid", spid);
+	    if (!parms.set("mypid", spid)) {
+		cerr << "Set mypid failed" << endl;
+	    }
 
 	    ostringstream ost;;
 	    ost << "mypid" << getpid();
-	    parms.set(ost.str(), spid, "");
-
-	    parms.set("unstring", "Une jolie phrase pour essayer");
+	    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) {
@@ -575,6 +673,17 @@
 		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);