Switch to side-by-side view

--- a/sc2src/conftree.cpp
+++ b/sc2src/conftree.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 J.F.Dockes
+/* Copyright (C) 2003-2016 J.F.Dockes
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
  *   the Free Software Foundation; either version 2 of the License, or
@@ -14,22 +14,34 @@
  *   Free Software Foundation, Inc.,
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
-#ifndef TEST_CONFTREE
+#ifdef BUILDING_RECOLL
+#include "autoconfig.h"
+#else
+#include "config.h"
+#endif
 
 #include "conftree.h"
 
-#include <fnmatch.h>                    // for fnmatch
-#include <stdlib.h>                     // for abort
-#include <sys/stat.h>                   // for stat, st_mtime
-#include <unistd.h>                     // for access
-#include <pwd.h>                        // for getpwnam, getpwuid, passwd
-
-#include <algorithm>                    // for find
-#include <cstring>                      // for strlen
-#include <fstream>                      // for ifstream, ofstream
-#include <iostream>                     // for cerr, cout
-#include <sstream>                      // for stringstream
-#include <utility>                      // for pair
+#include <ctype.h>
+#include <fnmatch.h>
+#ifdef _WIN32
+#include "safesysstat.h"
+#else
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#endif
+
+#include <algorithm>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <utility>
+
+#include "pathut.h"
+#include "smallut.h"
 
 using namespace std;
 
@@ -40,93 +52,19 @@
 #define LOGDEB(X)
 #endif
 
-#ifndef MIN
-#define MIN(A,B) ((A) < (B) ? (A) : (B))
-#endif
-
-#define LL 2048
-
-string ConfNull::path_home()
-{
-    uid_t uid = getuid();
-
-    struct passwd *entry = getpwuid(uid);
-    if (entry == 0) {
-        const char *cp = getenv("HOME");
-        if (cp) {
-            return cp;
-        } else {
-            return "/";
-        }
-    }
-
-    string homedir = entry->pw_dir;
-    path_catslash(homedir);
-    return homedir;
-}
-
-void ConfNull::path_catslash(string& s)
-{
-    if (s.empty() || s[s.length() - 1] != '/') {
-        s += '/';
-    }
-}
-
-string ConfNull::path_cat(const string& s1, const string& s2)
-{
-    string res = s1;
-    path_catslash(res);
-    res +=  s2;
-    return res;
-}
-
-string ConfNull::path_tildexpand(const string& s)
-{
-    if (s.empty() || s[0] != '~') {
-        return s;
-    }
-    string o = s;
-    if (s.length() == 1) {
-        o.replace(0, 1, path_home());
-    } else if (s[1] == '/') {
-        o.replace(0, 2, path_home());
-    } else {
-        string::size_type pos = s.find('/');
-        int l = (pos == string::npos) ? s.length() - 1 : pos - 1;
-        struct passwd *entry = getpwnam(s.substr(1, l).c_str());
-        if (entry) {
-            o.replace(0, l + 1, entry->pw_dir);
-        }
-    }
-    return o;
-}
-void ConfNull::trimstring(string& s, const char *ws)
-{
-    string::size_type pos = s.find_first_not_of(ws);
-    if (pos == string::npos) {
-        s.clear();
-        return;
-    }
-    s.replace(0, pos, string());
-
-    pos = s.find_last_not_of(ws);
-    if (pos != string::npos && pos != s.length() - 1) {
-        s.replace(pos + 1, string::npos, string());
-    }
-}
-
 void ConfSimple::parseinput(istream& input)
 {
     string submapkey;
-    char cline[LL];
+    string cline;
     bool appending = false;
     string line;
     bool eof = false;
 
     for (;;) {
-        cline[0] = 0;
-        input.getline(cline, LL - 1);
-        LOGDEB((stderr, "Parse:line: [%s] status %d\n", cline, int(status)));
+        cline.clear();
+        std::getline(input, cline);
+        LOGDEB((stderr, "Parse:line: [%s] status %d\n",
+                cline.c_str(), int(status)));
         if (!input.good()) {
             if (input.bad()) {
                 LOGDEB((stderr, "Parse: input.bad()\n"));
@@ -141,10 +79,11 @@
         }
 
         {
-            int ll = strlen(cline);
-            while (ll > 0 && (cline[ll - 1] == '\n' || cline[ll - 1] == '\r')) {
-                cline[ll - 1] = 0;
-                ll--;
+            string::size_type pos = cline.find_last_not_of("\n\r");
+            if (pos == string::npos) {
+                cline.clear();
+            } else if (pos != cline.length() - 1) {
+                cline.erase(pos + 1);
             }
         }
 
@@ -178,6 +117,8 @@
             } else {
                 submapkey = line;
             }
+            m_subkeys_unsorted.push_back(submapkey);
+
             // 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)
@@ -247,7 +188,7 @@
         // It seems that there is no separate 'create if not exists'
         // open flag. Have to truncate to create, but dont want to do
         // this to an existing file !
-        if (access(fname, 0) < 0) {
+        if (!path_exists(fname)) {
             mode |= ios::trunc;
         }
         input.open(fname, mode);
@@ -334,29 +275,45 @@
 }
 
 // Appropriately output a subkey (nm=="") or variable line.
-// Splits long lines
+// We can't make any assumption about the data except that it does not
+// contain line breaks.
+// Avoid long lines if possible (for hand-editing)
+// We used to break at arbitrary places, but this was ennoying for
+// files with pure UTF-8 encoding (some files can be binary anyway),
+// because it made later editing difficult, as the file would no
+// longer have a valid encoding.
+// Any ASCII byte would be a safe break point for utf-8, but could
+// break some other encoding with, e.g. escape sequences? So break at
+// whitespace (is this safe with all encodings?).
+// Note that the choice of break point does not affect the validity of
+// the file data (when read back by conftree), only its ease of
+// editing with a normal editor.
 static ConfSimple::WalkerCode varprinter(void *f, const string& nm,
         const string& value)
 {
-    ostream *output = (ostream *)f;
+    ostream& output = *((ostream *)f);
     if (nm.empty()) {
-        *output << "\n[" << value << "]\n";
+        output << "\n[" << value << "]\n";
     } else {
-        string value1;
-        if (value.length() < 60) {
-            value1 = value;
+        output << nm << " = ";
+        if (nm.length() + value.length() < 75) {
+            output << value;
         } else {
-            string::size_type pos = 0;
-            while (pos < value.length()) {
-                string::size_type len = MIN(60, value.length() - pos);
-                value1 += value.substr(pos, len);
-                pos += len;
-                if (pos < value.length()) {
-                    value1 += "\\\n";
+            string::size_type ll = 0;
+            for (string::size_type pos = 0; pos < value.length(); pos++) {
+                string::value_type c = value[pos];
+                output << c;
+                ll++;
+                // Break at whitespace if line too long and "a lot" of
+                // remaining data
+                if (ll > 50 && (value.length() - pos) > 10 &&
+                        (c == ' ' || c == '\t')) {
+                    ll = 0;
+                    output << "\\\n";
                 }
             }
         }
-        *output << nm << " = " << value1 << "\n";
+        output << "\n";
     }
     return ConfSimple::WALK_CONTINUE;
 }
@@ -678,8 +635,9 @@
 int ConfTree::get(const std::string& name, string& value, const string& sk)
 const
 {
-    if (sk.empty() || sk[0] != '/') {
-        //  LOGDEB((stderr, "ConfTree::get: looking in global space\n"));
+    if (sk.empty() || !path_isabsolute(sk)) {
+        // LOGDEB((stderr, "ConfTree::get: looking in global space for [%s]\n",
+        // sk.c_str()));
         return ConfSimple::get(name, value, sk);
     }
 
@@ -692,8 +650,8 @@
 
     // Look in subkey and up its parents until root ('')
     for (;;) {
-        //  LOGDEB((stderr,"ConfTree::get: 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;
         }
@@ -701,463 +659,13 @@
         if (pos != string::npos) {
             msk.replace(pos, string::npos, string());
         } else {
-            break;
+#ifdef _WIN32
+            if (msk.size() == 2 && isalpha(msk[0]) && msk[1] == ':') {
+                msk.clear();
+            } else
+#endif
+                break;
         }
     }
     return 0;
 }
-
-#else // TEST_CONFTREE
-
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sstream>
-#include <iostream>
-#include <vector>
-
-#include "conftree.h"
-#include "smallut.h"
-#include "readfile.h"
-
-using namespace std;
-
-static char *thisprog;
-
-bool complex_updates(const string& fn)
-{
-    int fd;
-    if ((fd = open(fn.c_str(), O_RDWR | O_TRUNC | O_CREAT, 0666)) < 0) {
-        perror("open/create");
-        return false;
-    }
-    close(fd);
-
-    ConfTree conf(fn.c_str());
-    if (!conf.ok()) {
-        cerr << "Config init failed" << endl;
-        return false;
-    }
-
-    conf.set("nm-1", "val-1", "");
-    conf.set("nm-2", "val-2", "");
-
-    conf.set("nm-1", "val1-1", "/dir1");
-    conf.set("nm-2", "val1-2", "/dir1");
-
-    conf.set("nm-1", "val2-1", "/dir2");
-    conf.set("nm-2", "val2-2", "/dir2");
-
-    conf.set("nm-1", "val11-1", "/dir1/dir1");
-    conf.set("nm-2", "val11-2", "/dir1/dir1");
-
-    conf.eraseKey("/dir2");
-    conf.set("nm-1", "val2-1", "/dir2");
-    conf.set("nm-2", "val2-2", "/dir2");
-
-    conf.erase("nm-1", "");
-    conf.erase("nm-2", "");
-    conf.eraseKey("/dir1");
-    conf.eraseKey("/dir2");
-    conf.eraseKey("/dir1/dir1");
-
-    conf.set("nm-1", "val1-1", "/dir1");
-    conf.set("nm-2", "val1-2", "/dir1");
-    conf.set("nm-1", "val-1", "");
-
-    conf.set("nm-1", "val2-1", "/dir2");
-    conf.set("nm-2", "val2-2", "/dir2");
-
-    conf.set("nm-1", "val11-1", "/dir1/dir1");
-    conf.set("nm-2", "val11-2", "/dir1/dir1");
-
-    conf.erase("nm-1", "/dir2");
-    conf.erase("nm-2", "/dir2");
-    conf.erase("nm-1", "/dir1/dir1");
-    conf.erase("nm-2", "/dir1/dir1");
-
-    string data;
-    file_to_string(fn, data, 0);
-    const string ref =
-        "nm-1 = val-1\n"
-        "[/dir1]\n"
-        "nm-1 = val1-1\n"
-        "nm-2 = val1-2\n"
-        ;
-    if (data.compare(ref)) {
-        cerr << "Final file:" << endl << data << endl << "Differs from ref:" <<
-             endl << ref << endl;
-        return false;
-    } else {
-        cout << "Updates test Ok" << endl;
-    }
-    return true;
-}
-
-ConfSimple::WalkerCode mywalker(void *, const string& nm, const string& value)
-{
-    if (nm.empty()) {
-        printf("\n[%s]\n", value.c_str());
-    } else {
-        printf("'%s' -> '%s'\n", nm.c_str(), value.c_str());
-    }
-    return ConfSimple::WALK_CONTINUE;
-}
-
-const char *longvalue =
-    "Donnees012345678901234567890123456789012345678901234567890123456789AA"
-    "0123456789012345678901234567890123456789012345678901234567890123456789FIN"
-    ;
-
-void memtest(ConfSimple& c)
-{
-    cout << "Initial:" << endl;
-    c.showall();
-    if (c.set("nom", "avec nl \n 2eme ligne", "")) {
-        fprintf(stderr, "set with embedded nl succeeded !\n");
-        exit(1);
-    }
-    if (!c.set(string("parm1"), string("1"), string("subkey1"))) {
-        fprintf(stderr, "Set error");
-        exit(1);
-    }
-    if (!c.set("sparm", "Parametre \"string\" bla", "s2")) {
-        fprintf(stderr, "Set error");
-        exit(1);
-    }
-    if (!c.set("long", longvalue, "")) {
-        fprintf(stderr, "Set error");
-        exit(1);
-    }
-
-    cout << "Final:" << endl;
-    c.showall();
-}
-
-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 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)
-{
-    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/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"
-    "-U : complex update test. Will erase the named file parameter\n"
-    ;
-
-void Usage()
-{
-    fprintf(stderr, "%s:%s\n", thisprog, usage);
-    exit(1);
-}
-static int     op_flags;
-#define OPT_MOINS 0x1
-#define OPT_w     0x2
-#define OPT_q     0x4
-#define OPT_s     0x8
-#define OPT_S     0x10
-#define OPT_d     0x20
-#define OPT_V     0x40
-#define OPT_a     0x80
-#define OPT_k     0x100
-#define OPT_E     0x200
-#define OPT_U      0x400
-
-int main(int argc, char **argv)
-{
-    const char *nm = 0;
-    const char *sub = 0;
-    const char *value = 0;
-
-    thisprog = argv[0];
-    argc--;
-    argv++;
-
-    while (argc > 0 && **argv == '-') {
-        (*argv)++;
-        if (!(**argv))
-            /* Cas du "adb - core" */
-        {
-            Usage();
-        }
-        while (**argv)
-            switch (*(*argv)++) {
-            case 'a':
-                op_flags |= OPT_a;
-                if (argc < 4) {
-                    Usage();
-                }
-                nm = *(++argv);
-                argc--;
-                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) {
-                    Usage();
-                }
-                nm = *(++argv);
-                argc--;
-                sub = *(++argv);
-                argc--;
-                goto b1;
-            case 's':
-                op_flags |= OPT_s;
-                break;
-            case 'S':
-                op_flags |= OPT_S;
-                break;
-            case 'V':
-                op_flags |= OPT_V;
-                break;
-            case 'U':
-                op_flags |= OPT_U;
-                break;
-            case 'w':
-                op_flags |= OPT_w;
-                break;
-
-            default:
-                Usage();
-                break;
-            }
-b1:
-        argc--;
-        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();
-        }
-        ConfSimple c;
-        memtest(c);
-        exit(0);
-    }
-
-    // Other tests use file(s) as backing store
-    if (argc < 1) {
-        Usage();
-    }
-
-    if (op_flags & OPT_U) {
-        exit(!complex_updates(argv[0]));
-    }
-    vector<string> flist;
-    while (argc--) {
-        flist.push_back(*argv++);
-    }
-    bool ro = !(op_flags & (OPT_w | OPT_a | OPT_d | OPT_E));
-    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);
-        }
-        vector<string>lst = conf->getSubKeys();
-        for (vector<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_E) {
-        exit(!eraseKey(conf, 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;
-        vector<string> strings;
-        if (!stringToStrings(source, strings)) {
-            cerr << "parse failed" << endl;
-            exit(1);
-        }
-
-        for (vector<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->showall();
-        //printf("WALK\n");conf->sortwalk(mywalker, 0);
-        printf("\nNAMES in global space:\n");
-        vector<string> names = conf->getNames("");
-        for (vector<string>::iterator it = names.begin();
-                it != names.end(); it++) {
-            cout << *it << " ";
-        }
-        cout << endl;
-        printf("\nNAMES in global space matching t* \n");
-        names = conf->getNames("", "t*");
-        for (vector<string>::iterator it = names.begin();
-                it != names.end(); it++) {
-            cout << *it << " ";
-        }
-        cout << endl;
-    }
-}
-
-#endif