828 lines
19 KiB
C++
Executable File
828 lines
19 KiB
C++
Executable File
/*
|
|
PIP - Platform Independent Primitives
|
|
Config parser
|
|
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include "piconfig.h"
|
|
#include "pifile.h"
|
|
#include "piiostring.h"
|
|
#include "pistring_std.h"
|
|
|
|
/*! \class PIConfig
|
|
* \brief Configuration file
|
|
* \details This class provide handle access to configuration file.
|
|
*
|
|
* \section PIConfig_sec0 Synopsis
|
|
* PIConfig reads configuration file and create internal dendritic
|
|
* representation of all entries of this file. You can easily read
|
|
* some values and write new.
|
|
* \image html piconfig.png
|
|
*
|
|
* %PIConfig supports also INI-style files with sections "[section]".
|
|
* In this case line with section name interpret as prefix to the next
|
|
* lines. For example, these configs are equal:
|
|
* \code
|
|
* ser.device = /dev/ttyS0
|
|
* ser.speed = 115200
|
|
* debug = true
|
|
* \endcode
|
|
* \code
|
|
* [ser]
|
|
* device = /dev/ttyS0
|
|
* speed = 115200
|
|
* []
|
|
* debug = true
|
|
* \endcode
|
|
*
|
|
* \section PIConfig_sec1 Concepts
|
|
* Each node of internal tree has type PIConfig::Entry. %PIConfig
|
|
* has one root element \a rootEntry(). Any entry of configuration file is a
|
|
* child of this element.
|
|
*
|
|
*/
|
|
|
|
/*! \class PIConfig::Entry
|
|
* \brief %Entry of configuration file
|
|
* \details This class is node of internal PIConfig tree.
|
|
* %Entry provide access to elements of PIConfig. Each entry has
|
|
* children or next properties:
|
|
* * name
|
|
* * value
|
|
* * type
|
|
* * comment
|
|
*
|
|
* Each property is a PIString. These properties forms from text line with
|
|
* format: \code{.cpp} <name> = <value> #<type> <comment> \endcode
|
|
* Type and comment are optional fields. Type is a single letter immediately
|
|
* after comment symbol "#". \n \n
|
|
* %Entry has many implicit convertions to common types: boolean, integers,
|
|
* float, double, PIString, PIStringList. \n \n
|
|
* Generally there is no need to create instance of %PIConfig::Entry manually,
|
|
* it returns by functions \a getValue() of \a PIConfig, \a PIConfig::Entry or
|
|
* \a PIConfig::Branch. If there is no suitable entry to return, reference to
|
|
* internal instance of %PIConfig::Entry with "default" value will be returned.
|
|
* \snippet piconfig.cpp PIConfig::Entry
|
|
*
|
|
*/
|
|
|
|
/*! \class PIConfig::Branch
|
|
* \brief %Branch is a list of entries of configuration file
|
|
* \details %Branch provides some features to get entries lists.
|
|
* \snippet piconfig.cpp PIConfig::Branch
|
|
*
|
|
*/
|
|
|
|
|
|
PIConfig::Entry PIConfig::Branch::_empty;
|
|
PIConfig::Entry PIConfig::Entry::_empty;
|
|
|
|
|
|
PIConfig::Branch PIConfig::Branch::allLeaves() {
|
|
Branch b;
|
|
b.delim = delim;
|
|
piForeach (Entry * i, *this) {
|
|
if (i->isLeaf()) b << i;
|
|
else allLeaves(b, i);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
PIConfig::Entry & PIConfig::Branch::getValue(const PIString & vname, const PIString & def, bool * exist) {
|
|
if (vname.isEmpty()) {
|
|
_empty.clear();
|
|
_empty.delim = delim;
|
|
if (exist != 0) *exist = false;
|
|
return _empty;
|
|
}
|
|
PIStringList tree = vname.split(delim);
|
|
PIString name = tree.front();
|
|
tree.pop_front();
|
|
Entry * ce = 0;
|
|
piForeach (Entry * i, *this)
|
|
if (i->_name == name) {
|
|
ce = i;
|
|
break;
|
|
}
|
|
if (ce == 0) {
|
|
_empty._name = vname;
|
|
_empty._value = def;
|
|
_empty.delim = delim;
|
|
if (exist != 0) *exist = false;
|
|
return _empty;
|
|
}
|
|
piForeach (PIString & i, tree) {
|
|
ce = ce->findChild(i);
|
|
if (ce == 0) {
|
|
_empty._name = vname;
|
|
_empty._value = def;
|
|
_empty.delim = delim;
|
|
if (exist != 0) *exist = false;
|
|
return _empty;
|
|
}
|
|
}
|
|
if (exist != 0) *exist = true;
|
|
return *ce;
|
|
}
|
|
|
|
|
|
PIConfig::Branch PIConfig::Branch::getValues(const PIString & name) {
|
|
Branch b;
|
|
b.delim = delim;
|
|
piForeach (Entry * i, *this) {
|
|
if (i->isLeaf()) {
|
|
if (i->_name.find(name) >= 0)
|
|
b << i;
|
|
} else {
|
|
piForeach (Entry * j, i->_children)
|
|
if (j->_name.find(name) >= 0)
|
|
b << j;
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
PIConfig::Branch PIConfig::Branch::getLeaves() {
|
|
Branch b;
|
|
b.delim = delim;
|
|
piForeach (Entry * i, *this)
|
|
if (i->isLeaf())
|
|
b << i;
|
|
return b;
|
|
}
|
|
|
|
|
|
PIConfig::Branch PIConfig::Branch::getBranches() {
|
|
Branch b;
|
|
b.delim = delim;
|
|
piForeach (Entry * i, *this)
|
|
if (!i->isLeaf())
|
|
b << i;
|
|
return b;
|
|
}
|
|
|
|
|
|
PIConfig::Branch & PIConfig::Branch::filter(const PIString & f) {
|
|
for (int i = 0; i < size_s(); ++i) {
|
|
if (at(i)->_name.find(f) < 0) {
|
|
remove(i);
|
|
--i;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
bool PIConfig::Branch::entryExists(const Entry * e, const PIString & name) const {
|
|
if (e->_children.isEmpty()) {
|
|
return (e->_name == name);
|
|
}
|
|
piForeachC (Entry * i, e->_children)
|
|
if (entryExists(i, name)) return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
PIConfig::Entry & PIConfig::Entry::getValue(const PIString & vname, const PIString & def, bool * exist) {
|
|
PIStringList tree = vname.split(delim);
|
|
Entry * ce = this;
|
|
piForeach (PIString & i, tree) {
|
|
ce = ce->findChild(i);
|
|
if (ce == 0) {
|
|
_empty._name = vname;
|
|
_empty._value = def;
|
|
_empty.delim = delim;
|
|
if (exist != 0) *exist = false;
|
|
return _empty;
|
|
}
|
|
}
|
|
if (exist != 0) *exist = true;
|
|
return *ce;
|
|
}
|
|
|
|
|
|
PIConfig::Branch PIConfig::Entry::getValues(const PIString & vname) {
|
|
Branch b;
|
|
b.delim = delim;
|
|
piForeach (Entry * i, _children)
|
|
if (i->_name.find(vname) >= 0)
|
|
b << i;
|
|
return b;
|
|
}
|
|
|
|
|
|
bool PIConfig::Entry::entryExists(const Entry * e, const PIString & name) const {
|
|
if (e->_children.isEmpty()) {
|
|
return (e->_name == name);
|
|
}
|
|
piForeachC (Entry * i, e->_children)
|
|
if (entryExists(i, name)) return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
void PIConfig::Entry::coutt(std::ostream & s, const PIString & p) const {
|
|
PIString nl = p + " ";
|
|
if (!_value.isEmpty()) s << p << _name << " = " << _value << std::endl;
|
|
else std::cout << p << _name << std::endl;
|
|
piForeachC (Entry * i, _children) i->coutt(s, nl);
|
|
}
|
|
|
|
|
|
void PIConfig::Entry::piCoutt(PICout s, const PIString & p) const {
|
|
PIString nl = p + " ";
|
|
if (!_value.isEmpty()) s << p << _name << " = " << _value << PICoutManipulators::NewLine;
|
|
else std::cout << p << _name << std::endl;
|
|
piForeachC (Entry * i, _children) i->piCoutt(s, nl);
|
|
}
|
|
|
|
|
|
PIConfig::PIConfig(const PIString & path, PIIODevice::DeviceMode mode) {
|
|
_init();
|
|
own_dev = true;
|
|
dev = new PIFile(path, mode);
|
|
if (!dev->isOpened())
|
|
dev->open(path, mode);
|
|
parse();
|
|
}
|
|
|
|
|
|
PIConfig::PIConfig(PIString * string, PIIODevice::DeviceMode mode) {
|
|
_init();
|
|
own_dev = true;
|
|
dev = new PIIOString(string, mode);
|
|
parse();
|
|
}
|
|
|
|
|
|
PIConfig::PIConfig(PIIODevice * device, PIIODevice::DeviceMode mode) {
|
|
_init();
|
|
own_dev = false;
|
|
dev = device;
|
|
if (dev) dev->open(mode);
|
|
parse();
|
|
}
|
|
|
|
|
|
PIConfig::PIConfig(const PIString & path, PIStringList dirs) {
|
|
_init();
|
|
internal = true;
|
|
own_dev = true;
|
|
dev = new PIFile(path, PIIODevice::ReadOnly);
|
|
incdirs = dirs;
|
|
incdirs << PIFile::fileInfo(path).dir();
|
|
while (!dev->isOpened()) {
|
|
if (dirs.isEmpty()) break;
|
|
PIString cp = dirs.back();
|
|
if (cp.endsWith("/") || cp.endsWith("\\")) cp.pop_back();
|
|
cp += "/" + path;
|
|
dev->open(cp, PIIODevice::ReadOnly);
|
|
dirs.pop_back();
|
|
}
|
|
if (!dev->isOpened()) {
|
|
delete dev;
|
|
dev = 0;
|
|
return;
|
|
}
|
|
parse();
|
|
}
|
|
|
|
|
|
PIConfig::~PIConfig() {
|
|
root.deleteBranch();
|
|
if (own_dev && dev) delete dev;
|
|
dev = 0;
|
|
piForeach (PIConfig * c, inc_devs)
|
|
delete c;
|
|
inc_devs.clear();
|
|
includes.clear();
|
|
}
|
|
|
|
|
|
bool PIConfig::open(const PIString & path, PIIODevice::DeviceMode mode) {
|
|
if (own_dev && dev) delete dev;
|
|
own_dev = true;
|
|
dev = new PIFile(path, mode);
|
|
if (!dev->isOpened())
|
|
dev->open(path, mode);
|
|
parse();
|
|
return dev->isOpened();
|
|
}
|
|
|
|
|
|
bool PIConfig::open(PIString * string, PIIODevice::DeviceMode mode) {
|
|
if (own_dev && dev) delete dev;
|
|
own_dev = true;
|
|
dev = new PIIOString(string, mode);
|
|
parse();
|
|
return true;
|
|
}
|
|
|
|
|
|
void PIConfig::_init() {
|
|
internal = false;
|
|
delim = PIStringAscii(".");
|
|
root.delim = delim;
|
|
empty.delim = delim;
|
|
empty._parent = 0;
|
|
}
|
|
|
|
|
|
void PIConfig::_clearDev() {
|
|
if (!dev) return;
|
|
if (PIString(dev->className()) == "PIFile") {((PIFile*)dev)->clear(); return;}
|
|
if (PIString(dev->className()) == "PIIOString") {((PIIOString*)dev)->clear(); return;}
|
|
}
|
|
|
|
|
|
void PIConfig::_flushDev() {
|
|
if (!dev) return;
|
|
if (PIString(dev->className()) == "PIFile") {((PIFile*)dev)->flush();}
|
|
}
|
|
|
|
|
|
bool PIConfig::_isEndDev() {
|
|
if (!dev) return true;
|
|
if (PIString(dev->className()) == "PIFile") {return ((PIFile*)dev)->isEnd();}
|
|
if (PIString(dev->className()) == "PIIOString") {return ((PIIOString*)dev)->isEnd();}
|
|
return true;
|
|
}
|
|
|
|
|
|
void PIConfig::_seekToBeginDev() {
|
|
if (!dev) return;
|
|
if (PIString(dev->className()) == "PIFile") {((PIFile*)dev)->seekToBegin(); return;}
|
|
if (PIString(dev->className()) == "PIIOString") {((PIIOString*)dev)->seekToBegin(); return;}
|
|
}
|
|
|
|
|
|
PIString PIConfig::_readLineDev() {
|
|
if (!dev) return PIString();
|
|
if (PIString(dev->className()) == "PIFile") {return ((PIFile*)dev)->readLine();}
|
|
if (PIString(dev->className()) == "PIIOString") {return ((PIIOString*)dev)->readLine();}
|
|
return PIString();
|
|
}
|
|
|
|
|
|
void PIConfig::_writeDev(const PIString & l) {
|
|
//piCout << "write \"" << l << "\"";
|
|
if (!dev) return;
|
|
if (PIString(dev->className()) == "PIFile") {*((PIFile*)dev) << (l); return;}
|
|
if (PIString(dev->className()) == "PIIOString") {((PIIOString*)dev)->writeString(l); return;}
|
|
dev->write(l.toByteArray());
|
|
}
|
|
|
|
|
|
bool PIConfig::isOpened() const {
|
|
if (dev) return dev->isOpened();
|
|
return false;
|
|
}
|
|
|
|
|
|
PIConfig::Entry & PIConfig::getValue(const PIString & vname, const PIString & def, bool * exist) {
|
|
PIStringList tree = vname.split(delim);
|
|
Entry * ce = &root;
|
|
piForeach (PIString & i, tree) {
|
|
ce = ce->findChild(i);
|
|
if (ce == 0) {
|
|
if (exist != 0) *exist = false;
|
|
empty._name = vname;
|
|
empty._value = def;
|
|
empty.delim = delim;
|
|
return empty;
|
|
}
|
|
}
|
|
if (exist != 0) *exist = true;
|
|
return *ce;
|
|
}
|
|
|
|
|
|
PIConfig::Branch PIConfig::getValues(const PIString & vname) {
|
|
Branch b;
|
|
b.delim = delim;
|
|
piForeach (Entry * i, root._children)
|
|
if (i->_name.find(vname) >= 0)
|
|
b << i;
|
|
return b;
|
|
};
|
|
|
|
|
|
void PIConfig::addEntry(const PIString & name, const PIString & value, const PIString & type, bool write) {
|
|
if (getValue(name)._parent != 0)
|
|
return;
|
|
bool toRoot = false;
|
|
PIStringList tree = name.split(delim);
|
|
PIString ename = tree.back();
|
|
tree.pop_back();
|
|
Entry * te, * ce, * entry = &root;
|
|
if (tree.isEmpty()) toRoot = true;
|
|
piForeach (PIString & i, tree) {
|
|
te = entry->findChild(i);
|
|
if (te == 0) {
|
|
ce = new Entry();
|
|
ce->delim = delim;
|
|
ce->_tab = entry->_tab;
|
|
ce->_line = entry->_line;
|
|
ce->_name = i;
|
|
ce->_parent = entry;
|
|
entry->_children << ce;
|
|
entry = ce;
|
|
} else entry = te;
|
|
}
|
|
PIConfig::Branch ch = entry->_children;
|
|
ch.sort(PIConfig::Entry::compare);
|
|
te = (entry->isLeaf() ? 0 : ch.back());
|
|
ce = new Entry();
|
|
ce->delim = delim;
|
|
ce->_name = ename;
|
|
ce->_value = value;
|
|
ce->_type = type;
|
|
if (te == 0) {
|
|
ce->_tab = entry->_tab;
|
|
if (toRoot) ce->_line = other.size_s() - 1;
|
|
else ce->_line = entry->_line;
|
|
} else {
|
|
ce->_tab = te->_tab;
|
|
if (toRoot) ce->_line = other.size_s() - 1;
|
|
else {
|
|
ch = entry->_parent->_children;
|
|
ch.sort(PIConfig::Entry::compare);
|
|
ce->_line = ch.back()->_line + 1;
|
|
}
|
|
}
|
|
ce->_parent = entry;
|
|
entry->_children << ce;
|
|
other.insert(ce->_line, "");
|
|
Branch b = allLeaves();
|
|
bool found = false;
|
|
for (int i = 0; i < b.size_s(); ++i) {
|
|
if (found) {
|
|
b[i]->_line++;
|
|
continue;
|
|
}
|
|
if (b[i] == ce) {
|
|
found = true;
|
|
if (i > 0)
|
|
if (b[i - 1]->_line == b[i]->_line)
|
|
b[i - 1]->_line++;
|
|
}
|
|
}
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::setValue(const PIString & name, const PIString & value, const PIString & type, bool write) {
|
|
Entry & e(getValue(name));
|
|
if (&e == &empty) {
|
|
addEntry(name, value, type, write);
|
|
return;
|
|
}
|
|
e._value = value;
|
|
e._type = type;
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
int PIConfig::entryIndex(const PIString & name) {
|
|
PIStringList tree = name.split(delim);
|
|
Entry * ce = &root;
|
|
piForeach (PIString & i, tree) {
|
|
ce = ce->findChild(i);
|
|
if (ce == 0)
|
|
return -1;
|
|
}
|
|
return allLeaves().indexOf(ce);
|
|
}
|
|
|
|
|
|
void PIConfig::setValue(uint number, const PIString & value, bool write) {
|
|
Entry & e(entryByIndex(number));
|
|
if (&e == &empty) return;
|
|
e._value = value;
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::setName(uint number, const PIString & name, bool write) {
|
|
Entry & e(entryByIndex(number));
|
|
if (&e == &empty) return;
|
|
e._name = name;
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::setType(uint number, const PIString & type, bool write) {
|
|
Entry & e(entryByIndex(number));
|
|
if (&e == &empty) return;
|
|
e._type = type;
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::setComment(uint number, const PIString & comment, bool write) {
|
|
Entry & e(entryByIndex(number));
|
|
if (&e == &empty) return;
|
|
e._comment = comment;
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::removeEntry(const PIString & name, bool write) {
|
|
Entry & e(getValue(name));
|
|
if (&e == &empty) return;
|
|
Branch b = allLeaves();
|
|
removeEntry(b, &e);
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::removeEntry(uint number, bool write) {
|
|
Entry & e(entryByIndex(number));
|
|
if (&e == &empty) return;
|
|
Branch b = allLeaves();
|
|
removeEntry(b, &e);
|
|
if (write) writeAll();
|
|
}
|
|
|
|
|
|
void PIConfig::removeEntry(Branch & b, PIConfig::Entry * e) {
|
|
bool leaf = true;
|
|
if (e->isLeaf()) other.remove(e->_line);
|
|
if (!e->isLeaf() && !e->_value.isEmpty()) {
|
|
e->_value.clear();
|
|
leaf = false;
|
|
} else {
|
|
int cc = e->_children.size_s();
|
|
piForTimes (cc)
|
|
removeEntry(b, e->_children.back());
|
|
}
|
|
bool found = false;
|
|
for (int i = 0; i < b.size_s(); ++i) {
|
|
if (found) {
|
|
b[i]->_line--;
|
|
continue;
|
|
}
|
|
if (b[i] == e) found = true;
|
|
}
|
|
if (!leaf) return;
|
|
e->_parent->_children.removeOne(e);
|
|
b.removeOne(e);
|
|
delete e;
|
|
}
|
|
|
|
|
|
PIString PIConfig::getPrefixFromLine(PIString line, bool * exists) {
|
|
line.trim();
|
|
if (line.left(1) == "#") {if (exists) *exists = false; return PIString();}
|
|
int ci = line.find("#");
|
|
if (ci >= 0) line.cutRight(line.size() - ci);
|
|
if (line.find("=") >= 0) {if (exists) *exists = false; return PIString();}
|
|
if (line.find("[") >= 0 && line.find("]") >= 0) {
|
|
if (exists) *exists = true;
|
|
return line.takeRange('[', ']').trim();
|
|
}
|
|
if (exists) *exists = false;
|
|
return PIString();
|
|
}
|
|
|
|
|
|
void PIConfig::writeAll() {
|
|
//cout << this << " write < " << size() << endl;
|
|
_clearDev();
|
|
//*this << "1234567894132456798\n"; return;
|
|
//writeEntry(&root);
|
|
buildFullNames(&root);
|
|
Branch b = allLeaves();
|
|
PIString prefix, tprefix;
|
|
bool isPrefix;
|
|
//for (int i = 0; i < b.size_s(); ++i)
|
|
// cout << b[i]->_name << " = " << b[i]->_value << endl;
|
|
int j = 0;
|
|
for (int i = 0; i < other.size_s(); ++i) {
|
|
//cout << j << endl;
|
|
if (j >= 0 && j < b.size_s()) {
|
|
if (b[j]->_line == i) {
|
|
b[j]->buildLine();
|
|
_writeDev((b[j]->_all).cutLeft(prefix.size()) + "\n");
|
|
//cout << this << " " << b[j]->_all << endl;
|
|
++j;
|
|
} else {
|
|
_writeDev(other[i]);
|
|
tprefix = getPrefixFromLine(other[i], &isPrefix);
|
|
if (isPrefix) {
|
|
prefix = tprefix;
|
|
if (!prefix.isEmpty())
|
|
prefix += delim;
|
|
}
|
|
if (i < other.size_s() - 1)
|
|
_writeDev('\n');
|
|
//cout << this << " " << other[i] << endl;
|
|
}
|
|
} else {
|
|
_writeDev(other[i]);
|
|
tprefix = getPrefixFromLine(other[i], &isPrefix);
|
|
if (isPrefix) {
|
|
prefix = tprefix;
|
|
if (!prefix.isEmpty())
|
|
prefix += delim;
|
|
}
|
|
if (i < other.size_s() - 1)
|
|
_writeDev('\n');
|
|
//cout << this << " " << other[i] << endl;
|
|
}
|
|
}
|
|
_flushDev();
|
|
readAll();
|
|
//cout << this << " write > " << size() << endl;
|
|
}
|
|
|
|
|
|
void PIConfig::clear() {
|
|
_clearDev();
|
|
parse();
|
|
}
|
|
|
|
|
|
void PIConfig::readAll() {
|
|
root.deleteBranch();
|
|
root.clear();
|
|
parse();
|
|
}
|
|
|
|
|
|
bool PIConfig::entryExists(const Entry * e, const PIString & name) const {
|
|
if (e->_children.isEmpty()) {
|
|
return (e->_name == name);
|
|
}
|
|
piForeachC (Entry * i, e->_children)
|
|
if (entryExists(i, name)) return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
void PIConfig::updateIncludes() {
|
|
if (internal) return;
|
|
all_includes.clear();
|
|
piForeach (PIConfig * c, includes)
|
|
all_includes << c->allLeaves();
|
|
}
|
|
|
|
|
|
PIString PIConfig::parseLine(PIString v) {
|
|
int i = -1, l = 0;
|
|
while (1) {
|
|
i = v.find("${");
|
|
if (i < 0) break;
|
|
PIString w = v.mid(i + 1).takeRange('{', '}'), r;
|
|
l = w.length() + 3;
|
|
w = parseLine(w);
|
|
w.trim();
|
|
bool ex = false;
|
|
PIConfig::Entry & me = getValue(w, "", &ex);
|
|
if (ex) {
|
|
r = me._value;
|
|
} else {
|
|
piForeachC (PIConfig::Entry * e, all_includes)
|
|
if (e->_full_name == w) {
|
|
r = e->_value;
|
|
break;
|
|
}
|
|
}
|
|
v.replace(i, l, r);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
|
|
void PIConfig::parse() {
|
|
//piCout << "[PIConfig] charset" << PIFile::defaultCharset();
|
|
PIString src, str, tab, comm, all, name, type, prefix, tprefix;
|
|
PIStringList tree;
|
|
Entry * entry, * te, * ce;
|
|
int ind, sind;
|
|
bool isNew, isPrefix;
|
|
piForeach (PIConfig * c, inc_devs)
|
|
delete c;
|
|
inc_devs.clear();
|
|
includes.clear();
|
|
if (!isOpened()) return;
|
|
_seekToBeginDev();
|
|
other.clear();
|
|
lines = 0;
|
|
while (!_isEndDev()) {
|
|
other.push_back(PIString());
|
|
src = str = parseLine(_readLineDev());
|
|
tprefix = getPrefixFromLine(src, &isPrefix);
|
|
if (isPrefix) {
|
|
prefix = tprefix;
|
|
if (!prefix.isEmpty())
|
|
prefix += delim;
|
|
}
|
|
//piCout << "line \"" << str << "\"";
|
|
tab = str.left(str.find(str.trimmed().left(1)));
|
|
str.trim();
|
|
//cout << endl << str << endl << endl;
|
|
// piCout << "[PIConfig] str" << str.size() << str << str.toUTF8();
|
|
all = str;
|
|
ind = str.find('=');
|
|
if ((ind > 0) && (str[0] != '#')) {
|
|
sind = str.find('#');
|
|
if (sind > 0) {
|
|
comm = str.right(str.length() - sind - 1).trimmed();
|
|
if (comm.length() > 0) type = comm[0];
|
|
else type = "s";
|
|
comm = comm.right(comm.length() - 1).trimmed();
|
|
str = str.left(sind);
|
|
} else {
|
|
type = "s";
|
|
comm = "";
|
|
}
|
|
|
|
//name = str.left(ind).trimmed();
|
|
tree = (prefix + str.left(ind).trimmed()).split(delim);
|
|
if (tree.front() == "include") {
|
|
name = str.right(str.length() - ind - 1).trimmed();
|
|
PIConfig * iconf = new PIConfig(name, incdirs);
|
|
//piCout << "include" << name << iconf->dev;
|
|
if (!iconf->dev) {
|
|
delete iconf;
|
|
} else {
|
|
inc_devs << iconf;
|
|
includes << iconf << iconf->includes;
|
|
updateIncludes();
|
|
}
|
|
//piCout << "includes" << includes;
|
|
other.back() = src;
|
|
} else {
|
|
name = tree.back();
|
|
tree.pop_back();
|
|
entry = &root;
|
|
piForeachC (PIString & i, tree) {
|
|
te = entry->findChild(i);
|
|
if (te == 0) {
|
|
ce = new Entry();
|
|
ce->delim = delim;
|
|
ce->_tab = tab;
|
|
ce->_line = lines;
|
|
ce->_name = i;
|
|
ce->_parent = entry;
|
|
entry->_children << ce;
|
|
entry = ce;
|
|
} else entry = te;
|
|
}
|
|
isNew = false;
|
|
ce = entry->findChild(name);
|
|
if (ce == 0) {
|
|
ce = new Entry();
|
|
isNew = true;
|
|
}
|
|
ce->delim = delim;
|
|
ce->_tab = tab;
|
|
ce->_name = name;
|
|
ce->_value = str.right(str.length() - ind - 1).trimmed();
|
|
ce->_type = type;
|
|
ce->_comment = comm;
|
|
//piCout << "[PIConfig] comm" << comm.size() << comm << comm.toUTF8();
|
|
//piCout << "[PIConfig] type" << type.size() << type << type.toUTF8();
|
|
ce->_line = lines;
|
|
ce->_all = all;
|
|
if (isNew) {
|
|
ce->_parent = entry;
|
|
entry->_children << ce;
|
|
}
|
|
}
|
|
} else other.back() = src;
|
|
lines++;
|
|
}
|
|
setEntryDelim(&root, delim);
|
|
buildFullNames(&root);
|
|
}
|
|
|
|
|
|
std::ostream &operator <<(std::ostream & s, const PIConfig::Entry & v) {
|
|
s << v.value();
|
|
return s;
|
|
}
|
|
|
|
|
|
std::ostream &operator <<(std::ostream & s, const PIConfig::Branch & v) {
|
|
v.coutt(s, "");
|
|
return s;
|
|
}
|