use conftree conversions

This commit is contained in:
Jean-Francois Dockes 2019-12-02 09:37:34 +01:00
parent a7058476a7
commit 414222c003
9 changed files with 456 additions and 453 deletions

View File

@ -970,15 +970,10 @@ bool RclConfig::readFieldsConfig(const string& cnferrloc)
"]: [" << val << "]\n");
return 0;
}
string tval;
if (attrs.get("wdfinc", tval))
ft.wdfinc = atoi(tval.c_str());
if (attrs.get("boost", tval))
ft.boost = atof(tval.c_str());
if (attrs.get("pfxonly", tval))
ft.pfxonly = stringToBool(tval);
if (attrs.get("noterms", tval))
ft.noterms = stringToBool(tval);
ft.wdfinc = attrs.getInt("wdfinc", 1);
ft.boost = attrs.getFloat("boost", 1.0);
ft.pfxonly = attrs.getBool("pfxonly", false);
ft.noterms = attrs.getBool("noterms", false);
m_fldtotraits[stringtolower(fieldname)] = ft;
LOGDEB2("readFieldsConfig: [" << fieldname << "] -> [" << ft.pfx <<
"] " << ft.wdfinc << " " << ft.boost << "\n");
@ -1018,11 +1013,7 @@ bool RclConfig::readFieldsConfig(const string& cnferrloc)
return 0;
}
}
int valuelen{0};
if (attrs.get("len", tval)) {
valuelen = atoi(tval.c_str());
}
int valuelen = attrs.getInt("len", 0);
// Find or insert traits entry
const auto pit =
m_fldtotraits.insert(

View File

@ -24,16 +24,12 @@
void readIdxStatus(RclConfig *config, DbIxStatus &status)
{
ConfSimple cs(config->getIdxStatusFile().c_str(), 1);
string val;
cs.get("phase", val);
status.phase = DbIxStatus::Phase(atoi(val.c_str()));
status.phase = DbIxStatus::Phase(cs.getInt("phase", 0));
cs.get("fn", status.fn);
cs.get("docsdone", &status.docsdone);
cs.get("filesdone", &status.filesdone);
cs.get("fileerrors", &status.fileerrors);
cs.get("dbtotdocs", &status.dbtotdocs);
cs.get("totfiles", &status.totfiles);
string shm("0");
cs.get("hasmonitor", shm);
status.hasmonitor = stringToBool(shm);
status.docsdone = cs.getInt("docsdone", 0);
status.filesdone = cs.getInt("filesdone", 0);
status.fileerrors = cs.getInt("fileerrors", 0);
status.dbtotdocs = cs.getInt("dbtotdocs", 0);
status.totfiles = cs.getInt("totfiles", 0);
status.hasmonitor = cs.getBool("hasmonitor", false);
}

View File

@ -289,14 +289,8 @@ void SpellW::showStats()
if (!theconfig)
return;
ConfSimple cs(theconfig->getIdxStatusFile().c_str(), 1);
DbIxStatus st;
cs.get("fn", st.fn);
cs.get("docsdone", &st.docsdone);
cs.get("filesdone", &st.filesdone);
cs.get("fileerrors", &st.fileerrors);
cs.get("dbtotdocs", &st.dbtotdocs);
cs.get("totfiles", &st.totfiles);
readIdxStatus(theconfig, st);
resTW->setRowCount(row+1);
resTW->setItem(row, 0,

View File

@ -32,7 +32,6 @@
#include "rcldb_p.h"
#include "rclquery.h"
#include "rclquery_p.h"
#include "conftree.h"
#include "smallut.h"
#include "chrono.h"
#include "searchdata.h"

View File

@ -37,7 +37,8 @@ AM_CPPFLAGS = -Wall -Wno-unused -std=c++11 \
-D_GNU_SOURCE \
$(DEFS)
noinst_PROGRAMS = textsplit utf8iter fstreewalk rclconfig hldata unac mbox
noinst_PROGRAMS = textsplit utf8iter fstreewalk rclconfig hldata unac mbox \
circache
textsplit_SOURCES = trtextsplit.cpp
textsplit_LDADD = ../librecoll.la
@ -59,3 +60,6 @@ unac_LDADD = ../librecoll.la
mbox_SOURCES = trmbox.cpp
mbox_LDADD = ../librecoll.la
circache_SOURCES = trcircache.cpp
circache_LDADD = ../librecoll.la

View File

@ -0,0 +1,245 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <memory>
#include "circache.h"
#include "fileudi.h"
#include "conftree.h"
#include "readfile.h"
#include "log.h"
#include "smallut.h"
using namespace std;
static char *thisprog;
static char usage [] =
" -c [-u] <dirname> <sizekbs>: create\n"
" -p <dirname> <apath> [apath ...] : put files\n"
" -d <dirname> : dump\n"
" -g [-i instance] [-D] <dirname> <udi>: get\n"
" -D: also dump data\n"
" -e <dirname> <udi> : erase\n"
" -a <targetdir> <dir> [<dir> ...]: append old content to target\n"
" The target should be first resized to hold all the data, else only\n"
" as many entries as capacity permit will be retained\n"
;
static void
Usage(FILE *fp = stderr)
{
fprintf(fp, "%s: usage:\n%s", thisprog, usage);
exit(1);
}
static int op_flags;
#define OPT_MOINS 0x1
#define OPT_c 0x2
#define OPT_p 0x8
#define OPT_g 0x10
#define OPT_d 0x20
#define OPT_i 0x40
#define OPT_D 0x80
#define OPT_u 0x100
#define OPT_e 0x200
#define OPT_a 0x800
int main(int argc, char **argv)
{
int instance = -1;
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;
break;
case 'c':
op_flags |= OPT_c;
break;
case 'D':
op_flags |= OPT_D;
break;
case 'd':
op_flags |= OPT_d;
break;
case 'e':
op_flags |= OPT_e;
break;
case 'g':
op_flags |= OPT_g;
break;
case 'i':
op_flags |= OPT_i;
if (argc < 2) {
Usage();
}
if ((sscanf(*(++argv), "%d", &instance)) != 1) {
Usage();
}
argc--;
goto b1;
case 'p':
op_flags |= OPT_p;
break;
case 'u':
op_flags |= OPT_u;
break;
default:
Usage();
break;
}
b1:
argc--;
argv++;
}
Logger::getTheLog("")->setLogLevel(Logger::LLDEB1);
if (argc < 1) {
Usage();
}
string dir = *argv++;
argc--;
CirCache cc(dir);
if (op_flags & OPT_c) {
if (argc != 1) {
Usage();
}
int64_t sizekb = atoi(*argv++);
argc--;
int flags = 0;
if (op_flags & OPT_u) {
flags |= CirCache::CC_CRUNIQUE;
}
if (!cc.create(sizekb * 1024, flags)) {
cerr << "Create failed:" << cc.getReason() << endl;
exit(1);
}
} else if (op_flags & OPT_a) {
if (argc < 1) {
Usage();
}
while (argc) {
string reason;
if (CirCache::append(dir, *argv++, &reason) < 0) {
cerr << reason << endl;
return 1;
}
argc--;
}
} else if (op_flags & OPT_p) {
if (argc < 1) {
Usage();
}
if (!cc.open(CirCache::CC_OPWRITE)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
while (argc) {
string fn = *argv++;
argc--;
char dic[1000];
string data, reason;
if (!file_to_string(fn, data, &reason)) {
cerr << "File_to_string: " << reason << endl;
exit(1);
}
string udi;
make_udi(fn, "", udi);
string cmd("xdg-mime query filetype ");
// Should do more quoting here...
cmd += "'" + fn + "'";
FILE *fp = popen(cmd.c_str(), "r");
char* buf=0;
size_t sz = 0;
if (::getline(&buf, &sz, fp) -1) {
cerr << "Could not read from xdg-mime output\n";
exit(1);
}
pclose(fp);
string mimetype(buf);
free(buf);
trimstring(mimetype, "\n\r");
cout << "Got [" << mimetype << "]\n";
string s;
ConfSimple conf(s);
conf.set("udi", udi);
conf.set("mimetype", mimetype);
//ostringstream str; conf.write(str); cout << str.str() << endl;
if (!cc.put(udi, &conf, data, 0)) {
cerr << "Put failed: " << cc.getReason() << endl;
cerr << "conf: [";
conf.write(cerr);
cerr << "]" << endl;
exit(1);
}
}
cc.open(CirCache::CC_OPREAD);
} else if (op_flags & OPT_g) {
if (!cc.open(CirCache::CC_OPREAD)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
while (argc) {
string udi = *argv++;
argc--;
string dic, data;
if (!cc.get(udi, dic, &data, instance)) {
cerr << "Get failed: " << cc.getReason() << endl;
exit(1);
}
cout << "Dict: [" << dic << "]" << endl;
if (op_flags & OPT_D) {
cout << "Data: [" << data << "]" << endl;
}
}
} else if (op_flags & OPT_e) {
if (!cc.open(CirCache::CC_OPWRITE)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
while (argc) {
string udi = *argv++;
argc--;
string dic, data;
if (!cc.erase(udi)) {
cerr << "Erase failed: " << cc.getReason() << endl;
exit(1);
}
}
} else if (op_flags & OPT_d) {
if (!cc.open(CirCache::CC_OPREAD)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
cc.dump();
} else {
Usage();
}
exit(0);
}

View File

@ -15,7 +15,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef TEST_CIRCACHE
#include "autoconfig.h"
#include "circache.h"
@ -235,13 +234,15 @@ public:
bool khEnter(const string& udi, int64_t ofs) {
UdiH h(udi);
LOGDEB2("Circache::khEnter: h " << h.asHexString() << " offs " << ofs << " udi [" << udi << "]\n");
LOGDEB2("Circache::khEnter: h " << h.asHexString() << " offs " <<
ofs << " udi [" << udi << "]\n");
pair<kh_type::iterator, kh_type::iterator> p = m_ofskh.equal_range(h);
if (p.first != m_ofskh.end() && p.first->first == h) {
for (kh_type::iterator it = p.first; it != p.second; it++) {
LOGDEB2("Circache::khEnter: col h " << it->first.asHexString() << ", ofs " << it->second << "\n");
LOGDEB2("Circache::khEnter: col h " << it->first.asHexString() <<
", ofs " << it->second << "\n");
if (it->second == ofs) {
// (h,offs) already there. Happens
LOGDEB2("Circache::khEnter: already there\n");
@ -255,8 +256,9 @@ public:
}
void khDump() {
for (kh_type::const_iterator it = m_ofskh.begin();
it != m_ofskh.end(); it++) {
LOGDEB("Circache::KHDUMP: " << it->first.asHexString() << " " << it->second << "\n");
it != m_ofskh.end(); it++) {
LOGDEB("Circache::KHDUMP: " << it->first.asHexString() << " " <<
it->second << "\n");
}
}
@ -268,7 +270,8 @@ public:
UdiH h(udi);
LOGDEB2("Circache::khFind: h " << h.asHexString() << " udi [" << udi << "]\n");
LOGDEB2("Circache::khFind: h " << h.asHexString() << " udi [" << udi <<
"]\n");
pair<kh_type::iterator, kh_type::iterator> p = m_ofskh.equal_range(h);
@ -280,7 +283,8 @@ public:
LOGDEB("KHFIND: SECOND END()\n");
}
if (!(p.first->first == h))
LOGDEB("KHFIND: NOKEY: " << p.first->first.asHexString() << " " << p.second->first.asHexString() << "\n");
LOGDEB("KHFIND: NOKEY: " << p.first->first.asHexString() << " " <<
p.second->first.asHexString() << "\n");
#endif
if (p.first == m_ofskh.end() || !(p.first->first == h)) {
@ -309,7 +313,7 @@ public:
// Clear entries for vector of udi/offs
bool khClear(const vector<pair<string, int64_t> >& udis) {
for (vector<pair<string, int64_t> >::const_iterator it = udis.begin();
it != udis.end(); it++) {
it != udis.end(); it++) {
khClear(*it);
}
return true;
@ -367,15 +371,15 @@ public:
ostringstream s;
s <<
"maxsize = " << m_maxsize << "\n" <<
"oheadoffs = " << m_oheadoffs << "\n" <<
"nheadoffs = " << m_nheadoffs << "\n" <<
"npadsize = " << m_npadsize << "\n" <<
"unient = " << m_uniquentries << "\n" <<
" " <<
" " <<
" " <<
"\0";
"maxsize = " << m_maxsize << "\n" <<
"oheadoffs = " << m_oheadoffs << "\n" <<
"nheadoffs = " << m_nheadoffs << "\n" <<
"npadsize = " << m_npadsize << "\n" <<
"unient = " << m_uniquentries << "\n" <<
" " <<
" " <<
" " <<
"\0";
int sz = int(s.str().size());
assert(sz < CIRCACHE_FIRSTBLOCK_SIZE);
@ -397,38 +401,33 @@ public:
lseek(m_fd, 0, 0);
if (read(m_fd, bf, CIRCACHE_FIRSTBLOCK_SIZE) !=
CIRCACHE_FIRSTBLOCK_SIZE) {
CIRCACHE_FIRSTBLOCK_SIZE) {
m_reason << "readfirstblock: read() failed: errno " << errno;
return false;
}
string s(bf, CIRCACHE_FIRSTBLOCK_SIZE);
ConfSimple conf(s, 1);
string value;
if (!conf.get("maxsize", value, cstr_null)) {
m_maxsize = conf.getInt("maxsize", -1);
if (m_maxsize == -1) {
m_reason << "readfirstblock: conf get maxsize failed";
return false;
}
m_maxsize = atoll(value.c_str());
if (!conf.get("oheadoffs", value, cstr_null)) {
m_oheadoffs = conf.getInt("oheadoffs", -1);
if (m_oheadoffs == -1) {
m_reason << "readfirstblock: conf get oheadoffs failed";
return false;
}
m_oheadoffs = atoll(value.c_str());
if (!conf.get("nheadoffs", value, cstr_null)) {
m_nheadoffs = conf.getInt("nheadoffs", -1);
if (m_nheadoffs == -1) {
m_reason << "readfirstblock: conf get nheadoffs failed";
return false;
}
m_nheadoffs = atoll(value.c_str());
if (!conf.get("npadsize", value, cstr_null)) {
m_npadsize = conf.getInt("npadsize", -1);
if (m_npadsize == -1) {
m_reason << "readfirstblock: conf get npadsize failed";
return false;
}
m_npadsize = atoll(value.c_str());
if (!conf.get("unient", value, cstr_null)) {
m_uniquentries = false;
} else {
m_uniquentries = stringToBool(value);
}
m_uniquentries = conf.getBool("unient", false);
return true;
}
@ -444,7 +443,7 @@ public:
headerformat, d.dicsize, d.datasize, d.padsize, d.flags);
if (lseek(m_fd, offset, 0) != offset) {
m_reason << "CirCache::weh: lseek(" << offset <<
") failed: errno " << errno;
") failed: errno " << errno;
return false;
}
if (write(m_fd, bf, CIRCACHE_HEADER_SIZE) != CIRCACHE_HEADER_SIZE) {
@ -473,7 +472,7 @@ public:
if (lseek(m_fd, offset, 0) != offset) {
m_reason << "readEntryHeader: lseek(" << offset <<
") failed: errno " << errno;
") failed: errno " << errno;
return CCScanHook::Error;
}
char bf[CIRCACHE_HEADER_SIZE];
@ -491,11 +490,11 @@ public:
if (sscanf(bf, headerformat, &d.dicsize, &d.datasize,
&d.padsize, &d.flags) != 4) {
m_reason << " readEntryHeader: bad header at " <<
offset << " [" << bf << "]";
offset << " [" << bf << "]";
return CCScanHook::Error;
}
LOGDEB2("Circache:readEntryHeader: dcsz " << d.dicsize << " dtsz " << d.datasize << " pdsz " << d.padsize <<
" flgs " << d.flags << "\n");
LOGDEB2("Circache:readEntryHeader: dcsz " << d.dicsize << " dtsz " <<
d.datasize << " pdsz " << d.padsize << " flgs " <<d.flags<<"\n");
return CCScanHook::Continue;
}
@ -526,7 +525,7 @@ public:
startoffset = CIRCACHE_FIRSTBLOCK_SIZE;
continue;
}
/* FALLTHROUGH */
/* FALLTHROUGH */
default:
return st;
}
@ -564,7 +563,7 @@ public:
}
startoffset += CIRCACHE_HEADER_SIZE + d.dicsize +
d.datasize + d.padsize;
d.datasize + d.padsize;
}
}
@ -597,7 +596,7 @@ public:
// at the right position
if (lseek(m_fd, offs, 0) != offs) {
m_reason << "CirCache::get: lseek(" << offs << ") failed: " <<
errno;
errno;
return false;
}
char *bf = 0;
@ -680,7 +679,8 @@ public:
const EntryHeaderData& d) {
headoffs = offs;
padsize = d.padsize;
LOGDEB2("CCScanHookRecord::takeone: offs " << headoffs << " padsize " << padsize << "\n");
LOGDEB2("CCScanHookRecord::takeone: offs " << headoffs << " padsize " <<
padsize << "\n");
return Continue;
}
};
@ -692,7 +692,8 @@ string CirCache::getpath()
bool CirCache::create(int64_t maxsize, int flags)
{
LOGDEB("CirCache::create: [" << m_dir << "] maxsz " << maxsize << " flags 0x" << std::hex << flags <<std::dec<<"\n");
LOGDEB("CirCache::create: [" << m_dir << "] maxsz " << maxsize <<
" flags 0x" << std::hex << flags <<std::dec<<"\n");
if (m_d == 0) {
LOGERR("CirCache::create: null data\n");
return false;
@ -703,19 +704,19 @@ bool CirCache::create(int64_t maxsize, int flags)
// Directory does not exist, create it
if (mkdir(m_dir.c_str(), 0777) < 0) {
m_d->m_reason << "CirCache::create: mkdir(" << m_dir <<
") failed" << " errno " << errno;
") failed" << " errno " << errno;
return false;
}
} else {
// If the file exists too, and truncate is not set, switch
// to open-mode. Still may need to update header params.
if (access(m_d->datafn(m_dir).c_str(), 0) >= 0 &&
!(flags & CC_CRTRUNCATE)) {
!(flags & CC_CRTRUNCATE)) {
if (!open(CC_OPWRITE)) {
return false;
}
if (maxsize == m_d->m_maxsize &&
((flags & CC_CRUNIQUE) != 0) == m_d->m_uniquentries) {
((flags & CC_CRUNIQUE) != 0) == m_d->m_uniquentries) {
LOGDEB("Header unchanged, no rewrite\n");
return true;
}
@ -733,9 +734,10 @@ bool CirCache::create(int64_t maxsize, int flags)
}
m_d->m_maxsize = maxsize;
m_d->m_uniquentries = ((flags & CC_CRUNIQUE) != 0);
LOGDEB2("CirCache::create: rewriting header with maxsize " << m_d->m_maxsize << " oheadoffs " <<
m_d->m_oheadoffs << " nheadoffs " << m_d->m_nheadoffs << " npadsize " << m_d->m_npadsize <<
" unient " << m_d->m_uniquentries << "\n");
LOGDEB2("CirCache::create: rewriting header with maxsize " <<
m_d->m_maxsize << " oheadoffs " << m_d->m_oheadoffs <<
" nheadoffs " << m_d->m_nheadoffs << " npadsize " <<
m_d->m_npadsize << " unient " << m_d->m_uniquentries <<"\n");
return m_d->writefirstblock();
}
// Else fallthrough to create file
@ -744,7 +746,7 @@ bool CirCache::create(int64_t maxsize, int flags)
if ((m_d->m_fd = ::open(m_d->datafn(m_dir).c_str(),
O_CREAT | O_RDWR | O_TRUNC | O_BINARY, 0666)) < 0) {
m_d->m_reason << "CirCache::create: open/creat(" <<
m_d->datafn(m_dir) << ") failed " << "errno " << errno;
m_d->datafn(m_dir) << ") failed " << "errno " << errno;
return false;
}
@ -755,9 +757,9 @@ bool CirCache::create(int64_t maxsize, int flags)
char buf[CIRCACHE_FIRSTBLOCK_SIZE];
memset(buf, 0, CIRCACHE_FIRSTBLOCK_SIZE);
if (::write(m_d->m_fd, buf, CIRCACHE_FIRSTBLOCK_SIZE) !=
CIRCACHE_FIRSTBLOCK_SIZE) {
CIRCACHE_FIRSTBLOCK_SIZE) {
m_d->m_reason << "CirCache::create: write header failed, errno "
<< errno;
<< errno;
return false;
}
return m_d->writefirstblock();
@ -778,7 +780,7 @@ bool CirCache::open(OpMode mode)
mode == CC_OPREAD ?
O_RDONLY | O_BINARY : O_RDWR | O_BINARY)) < 0) {
m_d->m_reason << "CirCache::open: open(" << m_d->datafn(m_dir) <<
") failed " << "errno " << errno;
") failed " << "errno " << errno;
return false;
}
return m_d->readfirstblock();
@ -789,9 +791,9 @@ public:
virtual status takeone(int64_t offs, const string& udi,
const EntryHeaderData& d) {
cout << "Scan: offs " << offs << " dicsize " << d.dicsize
<< " datasize " << d.datasize << " padsize " << d.padsize <<
" flags " << d.flags <<
" udi [" << udi << "]" << endl;
<< " datasize " << d.datasize << " padsize " << d.padsize <<
" flags " << d.flags <<
" udi [" << udi << "]" << endl;
return Continue;
}
};
@ -810,7 +812,7 @@ bool CirCache::dump()
return false;
case CCScanHook::Continue:
cout << "Scan returns Continue ?? " << CCScanHook::Continue << " " <<
getReason() << endl;
getReason() << endl;
return false;
case CCScanHook::Error:
cout << "Scan returns Error: " << getReason() << endl;
@ -837,7 +839,8 @@ public:
virtual status takeone(int64_t offs, const string& udi,
const EntryHeaderData& d) {
LOGDEB2("Circache:Scan: off " << offs << " udi [" << udi << "] dcsz " << d.dicsize << " dtsz " << d.datasize <<
LOGDEB2("Circache:Scan: off " << offs << " udi [" << udi << "] dcsz " <<
d.dicsize << " dtsz " << d.datasize <<
" pdsz " << d.padsize << " flgs " << d.flags << "\n");
if (!m_udi.compare(udi)) {
m_instance++;
@ -873,7 +876,7 @@ bool CirCache::get(const string& udi, string& dic, string *data, int instance)
EntryHeaderData d_good;
int64_t o_good = 0;
for (vector<int64_t>::iterator it = ofss.begin();
it != ofss.end(); it++) {
it != ofss.end(); it++) {
LOGDEB1("Circache::get: trying offs " << *it << "\n");
EntryHeaderData d;
string fudi;
@ -989,7 +992,8 @@ public:
virtual status takeone(int64_t offs, const string& udi,
const EntryHeaderData& d) {
LOGDEB2("Circache:ScanSpacer:off " << offs << " dcsz " << d.dicsize << " dtsz " << d.datasize <<
LOGDEB2("Circache:ScanSpacer:off " << offs << " dcsz " << d.dicsize <<
" dtsz " << d.datasize <<
" pdsz " << d.padsize << " udi[" << udi << "]\n");
sizeseen += CIRCACHE_HEADER_SIZE + d.dicsize + d.datasize + d.padsize;
squashed_udis.push_back(make_pair(udi, offs));
@ -1016,7 +1020,8 @@ bool CirCache::put(const string& udi, const ConfSimple *iconf,
string dic;
if (!iconf || !iconf->get("udi", dic) || dic.empty() || dic.compare(udi)) {
m_d->m_reason << "No/bad 'udi' entry in input dic";
LOGERR("Circache::put: no/bad udi: DIC:[" << dic << "] UDI [" << udi << "]\n");
LOGERR("Circache::put: no/bad udi: DIC:[" << dic << "] UDI [" << udi <<
"]\n");
return false;
}
@ -1060,12 +1065,13 @@ bool CirCache::put(const string& udi, const ConfSimple *iconf,
int64_t npadsize = 0;
bool extending = false;
LOGDEB("CirCache::put: nsz " << nsize << " oheadoffs " << m_d->m_oheadoffs << "\n");
LOGDEB("CirCache::put: nsz " << nsize << " oheadoffs " <<
m_d->m_oheadoffs << "\n");
// Check if we can recover some pad space from the (physically) previous
// entry.
int64_t recovpadsize = m_d->m_oheadoffs == CIRCACHE_FIRSTBLOCK_SIZE ?
0 : m_d->m_npadsize;
0 : m_d->m_npadsize;
if (recovpadsize != 0) {
// Need to read the latest entry's header, to rewrite it with a
// zero pad size
@ -1095,7 +1101,7 @@ bool CirCache::put(const string& udi, const ConfSimple *iconf,
if (nsize <= recovpadsize) {
// If the new entry fits entirely in the pad area from the
// latest one, no need to recycle stuff
LOGDEB("CirCache::put: new fits in old padsize " << recovpadsize << "\n");
LOGDEB("CirCache::put: new fits in old padsize " << recovpadsize <<"\n");
npadsize = recovpadsize - nsize;
} else if (st.st_size < m_d->m_maxsize) {
// Still growing the file.
@ -1105,11 +1111,12 @@ bool CirCache::put(const string& udi, const ConfSimple *iconf,
// Scan the file until we have enough space for the new entry,
// and determine the pad size up to the 1st preserved entry
int64_t scansize = nsize - recovpadsize;
LOGDEB("CirCache::put: scanning for size " << scansize << " from offs " << m_d->m_oheadoffs << "\n");
LOGDEB("CirCache::put: scanning for size " << scansize <<
" from offs " << m_d->m_oheadoffs << "\n");
CCScanHookSpacer spacer(scansize);
switch (m_d->scan(m_d->m_oheadoffs, &spacer)) {
case CCScanHook::Stop:
LOGDEB("CirCache::put: Scan ok, sizeseen " << spacer.sizeseen << "\n");
LOGDEB("CirCache::put: Scan ok, sizeseen " << spacer.sizeseen<<"\n");
npadsize = spacer.sizeseen - scansize;
break;
case CCScanHook::Eof:
@ -1124,7 +1131,8 @@ bool CirCache::put(const string& udi, const ConfSimple *iconf,
m_d->khClear(spacer.squashed_udis);
}
LOGDEB("CirCache::put: writing " << nsize << " at " << nwriteoffs << " padsize " << npadsize << "\n");
LOGDEB("CirCache::put: writing " << nsize << " at " << nwriteoffs <<
" padsize " << npadsize << "\n");
if (lseek(m_d->m_fd, nwriteoffs, 0) != nwriteoffs) {
m_d->m_reason << "CirCache::put: lseek failed: " << errno;
@ -1211,7 +1219,7 @@ bool CirCache::next(bool& eof)
// Skip to next header, using values stored from previous one
m_d->m_itoffs += CIRCACHE_HEADER_SIZE + m_d->m_ithd.dicsize +
m_d->m_ithd.datasize + m_d->m_ithd.padsize;
m_d->m_ithd.datasize + m_d->m_ithd.padsize;
// Looped back ?
if (m_d->m_itoffs == m_d->m_oheadoffs) {
@ -1268,7 +1276,7 @@ bool CirCache::getCurrent(string& udi, string& dic, string *data)
// Copy all entries from occ to ncc. Both are already open.
static bool copyall(std::shared_ptr<CirCache> occ,
std::shared_ptr<CirCache> ncc, int& nentries,
ostringstream& msg)
ostringstream& msg)
{
bool eof = false;
if (!occ->rewind(eof)) {
@ -1298,7 +1306,7 @@ static bool copyall(std::shared_ptr<CirCache> occ,
//cerr << "UDI: " << udi << endl;
if (!ncc->put(udi, &dic, data)) {
msg << "put failed: " << ncc->getReason() << " sdic [" << sdic <<
"]" << endl;
"]" << endl;
return false;
}
nentries++;
@ -1342,253 +1350,3 @@ int CirCache::append(const string ddir, const string& sdir, string *reason)
return nentries;
}
#else // TEST ->
#include "autoconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <memory>
#include "circache.h"
#include "fileudi.h"
#include "conftree.h"
#include "readfile.h"
#include "log.h"
#include "smallut.h"
using namespace std;
static char *thisprog;
static char usage [] =
" -c [-u] <dirname> <sizekbs>: create\n"
" -p <dirname> <apath> [apath ...] : put files\n"
" -d <dirname> : dump\n"
" -g [-i instance] [-D] <dirname> <udi>: get\n"
" -D: also dump data\n"
" -e <dirname> <udi> : erase\n"
" -a <targetdir> <dir> [<dir> ...]: append old content to target\n"
" The target should be first resized to hold all the data, else only\n"
" as many entries as capacity permit will be retained\n"
;
static void
Usage(FILE *fp = stderr)
{
fprintf(fp, "%s: usage:\n%s", thisprog, usage);
exit(1);
}
static int op_flags;
#define OPT_MOINS 0x1
#define OPT_c 0x2
#define OPT_p 0x8
#define OPT_g 0x10
#define OPT_d 0x20
#define OPT_i 0x40
#define OPT_D 0x80
#define OPT_u 0x100
#define OPT_e 0x200
#define OPT_a 0x800
int main(int argc, char **argv)
{
int instance = -1;
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;
break;
case 'c':
op_flags |= OPT_c;
break;
case 'D':
op_flags |= OPT_D;
break;
case 'd':
op_flags |= OPT_d;
break;
case 'e':
op_flags |= OPT_e;
break;
case 'g':
op_flags |= OPT_g;
break;
case 'i':
op_flags |= OPT_i;
if (argc < 2) {
Usage();
}
if ((sscanf(*(++argv), "%d", &instance)) != 1) {
Usage();
}
argc--;
goto b1;
case 'p':
op_flags |= OPT_p;
break;
case 'u':
op_flags |= OPT_u;
break;
default:
Usage();
break;
}
b1:
argc--;
argv++;
}
Logger::getTheLog("")->setLogLevel(Logger::LLDEB1);
if (argc < 1) {
Usage();
}
string dir = *argv++;
argc--;
CirCache cc(dir);
if (op_flags & OPT_c) {
if (argc != 1) {
Usage();
}
int64_t sizekb = atoi(*argv++);
argc--;
int flags = 0;
if (op_flags & OPT_u) {
flags |= CirCache::CC_CRUNIQUE;
}
if (!cc.create(sizekb * 1024, flags)) {
cerr << "Create failed:" << cc.getReason() << endl;
exit(1);
}
} else if (op_flags & OPT_a) {
if (argc < 1) {
Usage();
}
while (argc) {
string reason;
if (CirCache::append(dir, *argv++, &reason) < 0) {
cerr << reason << endl;
return 1;
}
argc--;
}
} else if (op_flags & OPT_p) {
if (argc < 1) {
Usage();
}
if (!cc.open(CirCache::CC_OPWRITE)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
while (argc) {
string fn = *argv++;
argc--;
char dic[1000];
string data, reason;
if (!file_to_string(fn, data, &reason)) {
cerr << "File_to_string: " << reason << endl;
exit(1);
}
string udi;
make_udi(fn, "", udi);
string cmd("xdg-mime query filetype ");
// Should do more quoting here...
cmd += "'" + fn + "'";
FILE *fp = popen(cmd.c_str(), "r");
char* buf=0;
size_t sz = 0;
::getline(&buf, &sz, fp);
pclose(fp);
string mimetype(buf);
free(buf);
trimstring(mimetype, "\n\r");
cout << "Got [" << mimetype << "]\n";
string s;
ConfSimple conf(s);
conf.set("udi", udi);
conf.set("mimetype", mimetype);
//ostringstream str; conf.write(str); cout << str.str() << endl;
if (!cc.put(udi, &conf, data, 0)) {
cerr << "Put failed: " << cc.getReason() << endl;
cerr << "conf: [";
conf.write(cerr);
cerr << "]" << endl;
exit(1);
}
}
cc.open(CirCache::CC_OPREAD);
} else if (op_flags & OPT_g) {
if (!cc.open(CirCache::CC_OPREAD)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
while (argc) {
string udi = *argv++;
argc--;
string dic, data;
if (!cc.get(udi, dic, &data, instance)) {
cerr << "Get failed: " << cc.getReason() << endl;
exit(1);
}
cout << "Dict: [" << dic << "]" << endl;
if (op_flags & OPT_D) {
cout << "Data: [" << data << "]" << endl;
}
}
} else if (op_flags & OPT_e) {
if (!cc.open(CirCache::CC_OPWRITE)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
while (argc) {
string udi = *argv++;
argc--;
string dic, data;
if (!cc.erase(udi)) {
cerr << "Erase failed: " << cc.getReason() << endl;
exit(1);
}
}
} else if (op_flags & OPT_d) {
if (!cc.open(CirCache::CC_OPREAD)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
cc.dump();
} else {
Usage();
}
exit(0);
}
#endif

View File

@ -57,6 +57,47 @@ using namespace std;
#define CONFDEB LOGDEB2
#endif
long long ConfNull::getInt(const std::string& name, long long dflt,
const std::string& sk)
{
string val;
if (!get(name, val, sk)) {
return dflt;
}
char *endptr;
long long ret = strtoll(val.c_str(), &endptr, 0);
if (endptr == val.c_str()) {
return dflt;
}
return ret;
}
double ConfNull::getFloat(const std::string& name, double dflt,
const std::string& sk)
{
string val;
if (!get(name, val, sk)) {
return dflt;
}
char *endptr;
double ret = strtod(val.c_str(), &endptr);
if (endptr == val.c_str()) {
return dflt;
}
return ret;
}
bool ConfNull::getBool(const std::string& name, bool dflt,
const std::string& sk)
{
string val;
if (!get(name, val, sk)) {
return dflt;
}
return stringToBool(val);
}
static const SimpleRegexp varcomment_rx("[ \t]*#[ \t]*([a-zA-Z0-9]+)[ \t]*=",
0, 1);
@ -275,30 +316,20 @@ int ConfSimple::get(const string& nm, string& value, const string& sk) const
}
// Find submap
map<string, map<string, string> >::const_iterator ss;
if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
const auto ss = m_submaps.find(sk);
if (ss == m_submaps.end()) {
return 0;
}
// Find named value
map<string, string>::const_iterator s;
if ((s = ss->second.find(nm)) == ss->second.end()) {
const auto s = ss->second.find(nm);
if (s == ss->second.end()) {
return 0;
}
value = s->second;
return 1;
}
int ConfSimple::get(const string& nm, int *value, const string& sk) const
{
string sval;
if (!get(nm, sval, sk)) {
return 0;
}
*value = atoi(sval.c_str());
return 1;
}
// Appropriately output a subkey (nm=="") or variable line.
// We can't make any assumption about the data except that it does not
// contain line breaks.
@ -314,7 +345,7 @@ int ConfSimple::get(const string& nm, int *value, const string& sk) const
// 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)
const string& value)
{
ostream& output = *((ostream *)f);
if (nm.empty()) {
@ -332,7 +363,7 @@ static ConfSimple::WalkerCode varprinter(void *f, const string& nm,
// Break at whitespace if line too long and "a lot" of
// remaining data
if (ll > 50 && (value.length() - pos) > 10 &&
(c == ' ' || c == '\t')) {
(c == ' ' || c == '\t')) {
ll = 0;
output << "\\\n";
}
@ -356,13 +387,13 @@ int ConfSimple::set(const std::string& nm, const std::string& value,
}
return write();
}
int ConfSimple::set(const string& nm, long long val,
const string& sk)
{
return this->set(nm, lltodecstr(val), sk);
}
// 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)
@ -377,9 +408,9 @@ int ConfSimple::i_set(const std::string& nm, const std::string& value,
return 0;
}
bool existing = false;
map<string, map<string, string> >::iterator ss;
auto ss = m_submaps.find(sk);
// Test if submap already exists, else create it, and insert variable:
if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
if (ss == m_submaps.end()) {
CONFDEB("ConfSimple::i_set: new submap\n");
map<string, string> submap;
submap[nm] = value;
@ -397,8 +428,7 @@ int ConfSimple::i_set(const std::string& nm, const std::string& value,
}
} else {
// Insert or update variable in existing map.
map<string, string>::iterator it;
it = ss->second.find(nm);
auto it = ss->second.find(nm);
if (it == ss->second.end()) {
ss->second.insert(pair<string, string>(nm, value));
} else {
@ -438,7 +468,7 @@ int ConfSimple::i_set(const std::string& nm, const std::string& value,
// This is not logically possible. The subkey must
// exist. We're doomed
std::cerr << "Logical failure during configuration variable "
"insertion" << endl;
"insertion" << endl;
abort();
}
}
@ -486,8 +516,8 @@ int ConfSimple::erase(const string& nm, const string& sk)
return 0;
}
map<string, map<string, string> >::iterator ss;
if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
auto ss = m_submaps.find(sk);
if (ss == m_submaps.end()) {
return 0;
}
@ -501,8 +531,8 @@ int ConfSimple::erase(const string& nm, const string& sk)
int ConfSimple::eraseKey(const string& sk)
{
vector<string> nms = getNames(sk);
for (vector<string>::iterator it = nms.begin(); it != nms.end(); it++) {
erase(*it, sk);
for (const auto& nm : nms) {
erase(nm, sk);
}
return write();
}
@ -523,21 +553,16 @@ ConfSimple::sortwalk(WalkerCode(*walker)(void *, const string&, const string&),
return WALK_STOP;
}
// For all submaps:
for (map<string, map<string, string> >::const_iterator sit =
m_submaps.begin();
sit != m_submaps.end(); sit++) {
for (const auto& submap : m_submaps) {
// Possibly emit submap name:
if (!sit->first.empty() && walker(clidata, string(), sit->first.c_str())
== WALK_STOP) {
if (!submap.first.empty() &&
walker(clidata, string(), submap.first.c_str()) == WALK_STOP) {
return WALK_STOP;
}
// Walk submap
const map<string, string>& sm = sit->second;
for (map<string, string>::const_iterator it = sm.begin(); it != sm.end();
it++) {
if (walker(clidata, it->first, it->second) == WALK_STOP) {
for (const auto& item : submap.second) {
if (walker(clidata, item.first, item.second) == WALK_STOP) {
return WALK_STOP;
}
}
@ -579,30 +604,29 @@ bool ConfSimple::write(ostream& out) const
return false;
}
string sk;
for (vector<ConfLine>::const_iterator it = m_order.begin();
it != m_order.end(); it++) {
switch (it->m_kind) {
for (const auto& confline : m_order) {
switch (confline.m_kind) {
case ConfLine::CFL_COMMENT:
case ConfLine::CFL_VARCOMMENT:
out << it->m_data << endl;
out << confline.m_data << endl;
if (!out.good()) {
return false;
}
break;
case ConfLine::CFL_SK:
sk = it->m_data;
sk = confline.m_data;
CONFDEB("ConfSimple::write: SK [" << sk << "]\n");
// 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;
out << "[" << confline.m_data << "]" << endl;
if (!out.good()) {
return false;
}
}
break;
case ConfLine::CFL_VAR:
string nm = it->m_data;
string nm = confline.m_data;
CONFDEB("ConfSimple::write: VAR [" << nm << "], sk [" <<sk<< "]\n");
// As erase() doesnt update m_order we can find unexisting
// variables, and must not output anything for them. Have
@ -617,7 +641,7 @@ bool ConfSimple::write(ostream& out) const
}
break;
}
CONFDEB("ConfSimple::write: no value: nm["<<nm<<"] sk["<<sk<< "]\n");
CONFDEB("ConfSimple::write: no value: nm["<<nm<<"] sk["<<sk<<"]\n");
break;
}
}
@ -638,17 +662,16 @@ vector<string> ConfSimple::getNames(const string& sk, const char *pattern) const
if (!ok()) {
return mylist;
}
map<string, map<string, string> >::const_iterator ss;
if ((ss = m_submaps.find(sk)) == m_submaps.end()) {
const auto ss = m_submaps.find(sk);
if (ss == m_submaps.end()) {
return mylist;
}
mylist.reserve(ss->second.size());
map<string, string>::const_iterator it;
for (it = ss->second.begin(); it != ss->second.end(); it++) {
if (pattern && 0 != fnmatch(pattern, it->first.c_str(), 0)) {
for (const auto& item : ss->second) {
if (pattern && 0 != fnmatch(pattern, item.first.c_str(), 0)) {
continue;
}
mylist.push_back(it->first);
mylist.push_back(item.first);
}
return mylist;
}
@ -660,9 +683,8 @@ vector<string> ConfSimple::getSubKeys() const
return mylist;
}
mylist.reserve(m_submaps.size());
map<string, map<string, string> >::const_iterator ss;
for (ss = m_submaps.begin(); ss != m_submaps.end(); ss++) {
mylist.push_back(ss->first);
for (const auto& submap : m_submaps) {
mylist.push_back(submap.first);
}
return mylist;
}
@ -670,10 +692,9 @@ vector<string> ConfSimple::getSubKeys() const
bool ConfSimple::hasNameAnywhere(const string& nm) const
{
vector<string>keys = getSubKeys();
for (vector<string>::const_iterator it = keys.begin();
it != keys.end(); it++) {
for (const auto& key : keys) {
string val;
if (get(nm, val, *it)) {
if (get(nm, val, key)) {
return true;
}
}
@ -687,24 +708,23 @@ bool ConfSimple::commentsAsXML(ostream& out)
out << "<confcomments>\n";
string sk;
for (vector<ConfLine>::const_iterator it = lines.begin();
it != lines.end(); it++) {
switch (it->m_kind) {
for (const auto& line : lines) {
switch (line.m_kind) {
case ConfLine::CFL_COMMENT:
case ConfLine::CFL_VARCOMMENT:
{
string::size_type pos = it->m_data.find_first_not_of("# ");
string::size_type pos = line.m_data.find_first_not_of("# ");
if (pos != string::npos) {
out << it->m_data.substr(pos) << endl;
out << line.m_data.substr(pos) << endl;
}
break;
}
case ConfLine::CFL_SK:
out << "<subkey>" << it->m_data << "</subkey>" << endl;
out << "<subkey>" << line.m_data << "</subkey>" << endl;
break;
case ConfLine::CFL_VAR:
out << "<varsetting>" << it->m_data << " = " <<
it->m_value << "</varsetting>" << endl;
out << "<varsetting>" << line.m_data << " = " <<
line.m_value << "</varsetting>" << endl;
break;
default:
break;
@ -721,7 +741,7 @@ bool ConfSimple::commentsAsXML(ostream& out)
// //////////////////////////////////////////////////////////////////////////
int ConfTree::get(const std::string& name, string& value, const string& sk)
const
const
{
if (sk.empty() || !path_isabsolute(sk)) {
LOGDEB2("ConfTree::get: looking in global space for [" <<
@ -757,4 +777,3 @@ const
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2006 J.F.Dockes
/* Copyright (C) 2006-2019 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
@ -24,21 +24,21 @@
*
* Lines like '[subkey]' in the file define subsections, with independant
* configuration namespaces. Only subsections holding at least one variable are
* significant (empty subsections may be deleted during an update, or not)
* significant (empty subsections may be deleted during an update, or not).
*
* Whitespace around name and value is insignificant.
* Whitespace around name and value is ignored.
*
* The names are case-sensitive but don't depend on it, this might change
* The names are case-sensitive but don't depend on it, this might change.
*
* Values can be queried for, or set.
*
* Any line without a '=' is a comment (a line like #var = value
* actually assigns a variable named '#var', which is not a big issue)
* Any line without a '=', or beginning with '[ \t]*#' is a comment.
*
* A configuration object can be created empty or by reading from a file or
* a string.
* All 'set' calls cause an immediate rewrite of the backing object if any
* (file or string)
*
* All 'set' calls normally cause an immediate rewrite of the backing
* object if any (file or string). This can be prevented with holdWrites().
*
* The ConfTree derived class interprets the subkeys as file paths and
* lets subdir keys hierarchically inherit the properties from
@ -46,7 +46,7 @@
*
* The ConfStack class stacks several Con(Simple/Tree) objects so that
* parameters from the top of the stack override the values from lower
* (useful to have central/personal config files)
* (useful to have central/personal config files).
*/
#include <time.h>
@ -62,7 +62,6 @@
#include <istream>
#include <ostream>
#endif
#include <iostream>
#include "pathut.h"
@ -83,7 +82,7 @@ public:
};
/**
* Virtual base class used to define an interface mostly useful for testing
* Virtual base class used to define the interface, and a few helper methods.
*/
class ConfNull {
public:
@ -93,9 +92,15 @@ public:
const std::string& sk = std::string()) const = 0;
virtual int set(const std::string& nm, const std::string& val,
const std::string& sk = std::string()) = 0;
virtual long long getInt(const std::string& name, long long dflt,
const std::string& sk = std::string());
virtual double getFloat(const std::string& name, double dflt,
const std::string& sk = std::string());
virtual bool getBool(const std::string& name, bool dflt,
const std::string& sk = std::string());
virtual bool ok() const = 0;
virtual std::vector<std::string> getNames(const std::string& sk,
const char* = 0)const = 0;
const char* = 0) const = 0;
virtual bool hasNameAnywhere(const std::string& nm) const = 0;
virtual int erase(const std::string&, const std::string&) = 0;
virtual int eraseKey(const std::string&) = 0;
@ -167,15 +172,6 @@ public:
virtual int get(const std::string& name, std::string& value,
const std::string& sk = std::string()) const override;
/**
* Get integer 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 std::string& name, int* value,
const std::string& sk = std::string()) const;
/**
* Set value for named string parameter in specified subsection (or global)
* @return 0 for error, 1 else
@ -302,8 +298,8 @@ protected:
StatusCode status;
private:
// Set if we're working with a file
std::string m_filename;
time_t m_fmtime;
std::string m_filename;
time_t m_fmtime;
// Configuration data submaps (one per subkey, the main data has a
// null subkey)
std::map<std::string, std::map<std::string, std::string> > m_submaps;
@ -311,9 +307,9 @@ private:
// Presentation data. We keep the comments, empty lines and
// variable and subkey ordering information in there (for
// rewriting the file while keeping hand-edited information)
std::vector<ConfLine> m_order;
std::vector<ConfLine> m_order;
// Control if we're writing to the backing store
bool m_holdWrites;
bool m_holdWrites;
void parseinput(std::istream& input);
bool write();
@ -369,15 +365,16 @@ public:
};
/**
* Use several config files, trying to get values from each in order. Used to
* have a central config, with possible overrides from more specific
* (ie personal) ones.
* Use several config files, trying to get values from each in order.
*
* Enables having a central/default config, with possible overrides
* from more specific (e.g. personal) ones.
*
* Notes: it's ok for some of the files not to 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 except for parameters only defined in the topmost
* file (it erases only from there).
* (bottom) 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 except for parameters
* only defined in the topmost file (it erases only from there).
*/
template <class T> class ConfStack : public ConfNull {
public:
@ -418,7 +415,7 @@ public:
return *this;
}
virtual bool sourceChanged() const override {
virtual bool sourceChanged() const override {
for (const auto& conf : m_confs) {
if (conf->sourceChanged()) {
return true;
@ -500,7 +497,7 @@ public:
return getNames1(sk, pattern, false);
}
virtual std::vector<std::string> getNamesShallow(const std::string& sk,
const char *patt = 0) const {
const char *patt = 0) const {
return getNames1(sk, patt, true);
}