Loading backends/yaml/yamlconf.cpp +151 −192 Original line number Diff line number Diff line Loading @@ -38,13 +38,6 @@ #include "yamlconf.h" #include "exception.h" namespace confplus { struct YamlStack { std::string anchor; std::unique_ptr<YamlStack> prevel; }; }; confplus::Yaml::Yaml(){ } Loading @@ -70,24 +63,20 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ throw "Failed to open file for writing!"; } // Detect whether a node is a sequence-of-mappings parent: // All children are leaf nodes and at least one has Elements > 1 auto isSeqMappingParent = [](const Config::ConfigData *node) -> bool { if (!node->childs()) return false; const Config::ConfigData *child = node->getChild(); size_t maxElems = 0; while (child) { if (child->childs()) return false; // has sub-children → not a flat seq-mapping if (child->getElements() > maxElems) maxElems = child->getElements(); child = child->next(); } return maxElems > 1; // A path node whose children are ALL numeric ("0","1",...) represents a // YAML sequence. Sequence-of-mappings and nested sequences are stored this // way by the stack-based loader. auto isNumber = [](const std::string &s) -> bool { if (s.empty()) return false; for (char c : s) if (c < '0' || c > '9') return false; return true; }; // Detect a simple sequence: leaf node with Elements > 1 whose parent is NOT a seq-mapping auto isSimpleSequence = [](const Config::ConfigData *node) -> bool { return !node->childs() && node->getElements() > 1; auto allNumericChildren = [&](const Config::ConfigData *node) -> bool { const Config::ConfigData *c = node->getChild(); if (!c) return false; for (; c; c = c->next()) if (!isNumber(c->getKey())) return false; return true; }; // Get value at position from a ConfigData node Loading Loading @@ -130,81 +119,93 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ return s; }; std::string indent; // Recursive writer std::function<void(const Config::ConfigData*, int)> writeNode; writeNode = [&](const Config::ConfigData *node, int depth) { std::string pad(depth * 2, ' '); while (node) { if (isSeqMappingParent(node)) { // Sequence-of-mappings: collect all child keys, emit as array of mappings f << pad << node->getKey() << ":\n"; // Collect children and find max elements std::vector<const Config::ConfigData*> children; size_t maxElems = 0; for (const Config::ConfigData *c = node->getChild(); c; c = c->next()) { children.push_back(c); if (c->getElements() > maxElems) maxElems = c->getElements(); std::function<void(const Config::ConfigData*, const std::string&)> emitMapping; std::function<void(const Config::ConfigData*, const std::string&)> emitSequence; std::function<void(const Config::ConfigData*, const std::string&)> emitSeqElement; // Emit a chain of sibling nodes as a YAML mapping at indent `pad`. emitMapping = [&](const Config::ConfigData *first, const std::string &pad) { for (const Config::ConfigData *n = first; n; n = n->next()) { if (n->childs()) { f << pad << n->getKey() << ":\n"; if (allNumericChildren(n)) emitSequence(n, pad); else emitMapping(n->getChild(), pad + " "); } else if (n->getElements() > 1) { // simple sequence of scalars f << pad << n->getKey() << ":\n"; for (size_t i = 0; i < n->getElements(); ++i) f << pad << " - " << quoteValue(getValueAt(n, i)) << "\n"; } else { f << pad << n->getKey() << ": " << quoteValue(getValueAt(n, 0)) << "\n"; } } }; std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < maxElems; ++i) { bool first = true; for (auto *c : children) { std::string val = getValueAt(c, i); if (val.empty() && i >= c->getElements()) continue; if (first) { f << innerPad << "- " << c->getKey() << ": " << quoteValue(val) << "\n"; first = false; // Emit a path node whose children are numeric indices as a YAML sequence. emitSequence = [&](const Config::ConfigData *node, const std::string &pad) { std::string ipad = pad + " "; for (const Config::ConfigData *el = node->getChild(); el; el = el->next()) { if (el->childs()) { if (allNumericChildren(el)) { f << ipad << "-\n"; emitSequence(el, ipad); } else { f << innerPad << " " << c->getKey() << ": " << quoteValue(val) << "\n"; } emitSeqElement(el->getChild(), ipad); } } else if (el->getElements() > 1) { for (size_t i = 0; i < el->getElements(); ++i) f << ipad << "- " << quoteValue(getValueAt(el, i)) << "\n"; } else { f << ipad << "- " << quoteValue(getValueAt(el, 0)) << "\n"; } } else if (node->childs()) { // Mapping node with children f << pad << node->getKey() << ":\n"; writeNode(node->getChild(), depth + 1); } else if (isSimpleSequence(node)) { // Simple sequence (list of scalars) f << pad << node->getKey() << ":\n"; std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < node->getElements(); ++i) { f << innerPad << "- " << quoteValue(getValueAt(node, i)) << "\n"; } }; // Emit a mapping that is one element of a sequence: the first key line is // prefixed with "- ", subsequent keys align with two spaces. emitSeqElement = [&](const Config::ConfigData *first, const std::string &pad) { bool firstKey = true; for (const Config::ConfigData *n = first; n; n = n->next()) { std::string marker = firstKey ? "- " : " "; if (n->childs()) { f << pad << marker << n->getKey() << ":\n"; if (allNumericChildren(n)) emitSequence(n, pad + " "); else emitMapping(n->getChild(), pad + " "); } else if (n->getElements() > 1) { f << pad << marker << n->getKey() << ":\n"; for (size_t i = 0; i < n->getElements(); ++i) f << pad << " - " << quoteValue(getValueAt(n, i)) << "\n"; } else { // Simple scalar value f << pad << node->getKey() << ": " << quoteValue(getValueAt(node, 0)) << "\n"; f << pad << marker << n->getKey() << ": " << quoteValue(getValueAt(n, 0)) << "\n"; } node = node->next(); firstKey = false; } }; // Add blank lines between top-level sections for readability // Top-level: blank lines between sections for readability const Config::ConfigData *top = conf->first(); bool firstTop = true; while (top) { for (const Config::ConfigData *n = top; n; n = n->next()) { if (!firstTop) f << "\n"; firstTop = false; if (top->childs()) { f << top->getKey() << ":\n"; writeNode(top->getChild(), 1); } else if (isSimpleSequence(top)) { f << top->getKey() << ":\n"; for (size_t i = 0; i < top->getElements(); ++i) { f << " - " << quoteValue(getValueAt(top, i)) << "\n"; } if (n->childs()) { f << n->getKey() << ":\n"; if (allNumericChildren(n)) emitSequence(n, ""); else emitMapping(n->getChild(), " "); } else if (n->getElements() > 1) { f << n->getKey() << ":\n"; for (size_t i = 0; i < n->getElements(); ++i) f << " - " << quoteValue(getValueAt(n, i)) << "\n"; } else { f << top->getKey() << ": " << quoteValue(getValueAt(top, 0)) << "\n"; f << n->getKey() << ": " << quoteValue(getValueAt(n, 0)) << "\n"; } top = top->next(); } f.close(); Loading @@ -212,7 +213,7 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ void confplus::Yaml::loadConfig(const char* path, Config* conf) { FILE* fh = fopen(path, "rb"); yaml_event_type_t type; yaml_event_type_t type = YAML_NO_EVENT; yaml_parser_t parse; /* Initialize parser */ Loading @@ -227,134 +228,92 @@ void confplus::Yaml::loadConfig(const char* path, Config* conf) { /* Set input file */ yaml_parser_set_input_file(&parse, fh); std::unique_ptr<YamlStack> ystack = nullptr; // Stack-based parser. Each frame is either a mapping or a sequence. // Paths are built from the stack; sequence elements get a numeric index // segment, e.g. /BLOGI/DOMAINS/0/NAMES/1. This produces a clean, fully // indexed tree that supports arbitrary nesting (sequences inside mappings // inside sequences, etc.). struct Frame { bool isSeq; std::string path; // path of this container std::string key; // pending key (mapping only) bool keySet; // whether a key is waiting for its value int seqIndex; // next index (sequence only) }; std::vector<Frame> st; // Compute the path for a new child container under `top`, consuming the // pending mapping key or advancing the sequence index. auto childPath = [](Frame &top) -> std::string { if (top.isSeq) { std::string p = top.path + "/" + std::to_string(top.seqIndex); ++top.seqIndex; return p; } std::string p = top.path + "/" + top.key; top.key.clear(); top.keySet = false; return p; }; bool seq = false; bool seqMapping = false; bool gotValue = false; int seqIndex = 0; std::string key, value; int pos = 0; do { yaml_event_t event; if (!yaml_parser_parse(&parse, &event)) break; type = event.type; switch (event.type) { case YAML_MAPPING_START_EVENT: { if (seq && !seqMapping) { // First mapping inside a sequence: sequence-of-mappings mode seqMapping = true; seqIndex = 0; std::unique_ptr<YamlStack> cystack(new YamlStack); cystack->prevel = std::move(ystack); ystack = std::move(cystack); ystack->anchor = key; key.clear(); value.clear(); } else if (seqMapping) { // Subsequent mapping in the sequence key.clear(); value.clear(); } else { std::unique_ptr<YamlStack> cystack(new YamlStack); cystack->prevel = std::move(ystack); ystack = std::move(cystack); ystack->anchor = key; key = value; value.clear(); } std::string p = st.empty() ? std::string("") : childPath(st.back()); st.push_back(Frame{false, p, "", false, 0}); } break; case YAML_MAPPING_END_EVENT: { if (seqMapping) { // Don't pop stack; increment index for next mapping ++seqIndex; key.clear(); value.clear(); } else if (ystack && ystack->prevel) { ystack = std::move(ystack->prevel); } }break; case YAML_SCALAR_EVENT: { if (key.empty() && !gotValue) std::copy(event.data.scalar.value, event.data.scalar.value + event.data.scalar.length, std::inserter<std::string>(key, std::begin(key))); else { std::copy(event.data.scalar.value, event.data.scalar.value + event.data.scalar.length, std::inserter<std::string>(value, std::begin(value))); gotValue = true; } if (!st.empty()) st.pop_back(); } break; case YAML_SEQUENCE_START_EVENT: { seq = true; std::string p = st.empty() ? std::string("") : childPath(st.back()); st.push_back(Frame{true, p, "", false, 0}); } break; case YAML_SEQUENCE_END_EVENT: { if (seqMapping) { // Pop the stack that was pushed for the first mapping if (ystack && ystack->prevel) { ystack = std::move(ystack->prevel); if (!st.empty()) st.pop_back(); } break; case YAML_SCALAR_EVENT: { std::string s(reinterpret_cast<const char*>(event.data.scalar.value), event.data.scalar.length); if (st.empty()) break; Frame &top = st.back(); if (top.isSeq) { // Scalar element of a sequence -> store at the sequence path // with the running index as positional value. if (!top.path.empty()) { Config::ConfigData *k = conf->setKey(top.path); conf->setValue(k, top.seqIndex, s); } ++top.seqIndex; } else if (!top.keySet) { // Mapping key top.key = s; top.keySet = true; } else { // Mapping value std::string kp = top.path + "/" + top.key; if (kp.size() > 1) { Config::ConfigData *k = conf->setKey(kp); conf->setValue(k, 0, s); } seqMapping = false; seqIndex = 0; top.key.clear(); top.keySet = false; } seq = false; key = value; pos = 0; value.clear(); } break; default: break; } if (!key.empty() && gotValue) { std::vector<std::string> path_elements; for (YamlStack* curst = ystack.get(); curst; curst = curst->prevel.get()) { path_elements.push_back(curst->anchor); } std::string cname = ""; for (int i = path_elements.size() - 1; i >= 0; --i) { if (!path_elements[i].empty()) { cname += "/"; cname += path_elements[i]; } } if (!key.empty()) { cname += "/"; cname += key; } if (cname.empty()) { cname = "/"; cname += key; } Config::ConfigData* ckey = conf->setKey(cname); if (seqMapping) { conf->setValue(ckey, seqIndex, value); value.clear(); key.clear(); gotValue = false; } else { conf->setValue(ckey, pos, value); value.clear(); gotValue = false; if (!seq) { key.clear(); } else { ++pos; } } } type = event.type; yaml_event_delete(&event); } while (type != YAML_STREAM_END_EVENT); Loading src/conf.cpp +10 −2 Original line number Diff line number Diff line Loading @@ -43,8 +43,16 @@ confplus::Config::ConfigValue::ConfigValue(std::string value,size_t pos){ } confplus::Config::ConfigData* confplus::Config::getKey(const std::string& key) const { ConfigData* cdat = firstData.get(); if (key[0] != '/') { return getKey(key, nullptr); } confplus::Config::ConfigData* confplus::Config::getKey(const std::string& key, const ConfigData* base) const { // base == nullptr -> absolute lookup starting at the document root. // base != nullptr -> relative lookup starting at base's children, so a // path like "/NAMES/1" resolves underneath that node. ConfigData* cdat = base ? const_cast<ConfigData*>(base->Child.get()) : firstData.get(); if (key.empty() || key[0] != '/') { ConfException exp; exp[ConfException::Error] << "Config: getkey wrong path!"; throw exp; Loading src/conf.h +1 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ namespace confplus { ConfigData *getKey(const std::string &key) const; ConfigData *getKey(const std::string &key, const ConfigData *base) const; ConfigData *setKey(const std::string &key); void delKey(ConfigData *key); Loading tools/dumptree.cpp 0 → 100644 +32 −0 Original line number Diff line number Diff line #include <iostream> #include <string> #include "conf.h" #include "exception.h" static void dump(const confplus::Config &conf, const confplus::Config::ConfigData *node, const std::string &path){ for(const confplus::Config::ConfigData *cur=node; cur; cur=cur->next()){ std::string p = path + "/" + cur->getKey(); if(cur->childs()){ std::cout << p << " (path, elements=" << cur->getElements() << ")\n"; dump(conf, cur->getChild(), p); }else{ std::cout << p << " (leaf, elements=" << cur->getElements() << ") = ["; for(size_t i=0;i<cur->getElements();++i){ try{ std::cout << (i?", ":"") << conf.getValue(cur,i); }catch(...){} } std::cout << "]\n"; } } } int main(int argc,char *argv[]){ if(argc<2){ std::cerr << "usage: dumptree <backend:path>\n"; return 1; } try{ confplus::Config conf(argv[1]); dump(conf, conf.first(), ""); }catch(confplus::ConfException &e){ std::cerr << e.what() << std::endl; return 1; } return 0; } Loading
backends/yaml/yamlconf.cpp +151 −192 Original line number Diff line number Diff line Loading @@ -38,13 +38,6 @@ #include "yamlconf.h" #include "exception.h" namespace confplus { struct YamlStack { std::string anchor; std::unique_ptr<YamlStack> prevel; }; }; confplus::Yaml::Yaml(){ } Loading @@ -70,24 +63,20 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ throw "Failed to open file for writing!"; } // Detect whether a node is a sequence-of-mappings parent: // All children are leaf nodes and at least one has Elements > 1 auto isSeqMappingParent = [](const Config::ConfigData *node) -> bool { if (!node->childs()) return false; const Config::ConfigData *child = node->getChild(); size_t maxElems = 0; while (child) { if (child->childs()) return false; // has sub-children → not a flat seq-mapping if (child->getElements() > maxElems) maxElems = child->getElements(); child = child->next(); } return maxElems > 1; // A path node whose children are ALL numeric ("0","1",...) represents a // YAML sequence. Sequence-of-mappings and nested sequences are stored this // way by the stack-based loader. auto isNumber = [](const std::string &s) -> bool { if (s.empty()) return false; for (char c : s) if (c < '0' || c > '9') return false; return true; }; // Detect a simple sequence: leaf node with Elements > 1 whose parent is NOT a seq-mapping auto isSimpleSequence = [](const Config::ConfigData *node) -> bool { return !node->childs() && node->getElements() > 1; auto allNumericChildren = [&](const Config::ConfigData *node) -> bool { const Config::ConfigData *c = node->getChild(); if (!c) return false; for (; c; c = c->next()) if (!isNumber(c->getKey())) return false; return true; }; // Get value at position from a ConfigData node Loading Loading @@ -130,81 +119,93 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ return s; }; std::string indent; // Recursive writer std::function<void(const Config::ConfigData*, int)> writeNode; writeNode = [&](const Config::ConfigData *node, int depth) { std::string pad(depth * 2, ' '); while (node) { if (isSeqMappingParent(node)) { // Sequence-of-mappings: collect all child keys, emit as array of mappings f << pad << node->getKey() << ":\n"; // Collect children and find max elements std::vector<const Config::ConfigData*> children; size_t maxElems = 0; for (const Config::ConfigData *c = node->getChild(); c; c = c->next()) { children.push_back(c); if (c->getElements() > maxElems) maxElems = c->getElements(); std::function<void(const Config::ConfigData*, const std::string&)> emitMapping; std::function<void(const Config::ConfigData*, const std::string&)> emitSequence; std::function<void(const Config::ConfigData*, const std::string&)> emitSeqElement; // Emit a chain of sibling nodes as a YAML mapping at indent `pad`. emitMapping = [&](const Config::ConfigData *first, const std::string &pad) { for (const Config::ConfigData *n = first; n; n = n->next()) { if (n->childs()) { f << pad << n->getKey() << ":\n"; if (allNumericChildren(n)) emitSequence(n, pad); else emitMapping(n->getChild(), pad + " "); } else if (n->getElements() > 1) { // simple sequence of scalars f << pad << n->getKey() << ":\n"; for (size_t i = 0; i < n->getElements(); ++i) f << pad << " - " << quoteValue(getValueAt(n, i)) << "\n"; } else { f << pad << n->getKey() << ": " << quoteValue(getValueAt(n, 0)) << "\n"; } } }; std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < maxElems; ++i) { bool first = true; for (auto *c : children) { std::string val = getValueAt(c, i); if (val.empty() && i >= c->getElements()) continue; if (first) { f << innerPad << "- " << c->getKey() << ": " << quoteValue(val) << "\n"; first = false; // Emit a path node whose children are numeric indices as a YAML sequence. emitSequence = [&](const Config::ConfigData *node, const std::string &pad) { std::string ipad = pad + " "; for (const Config::ConfigData *el = node->getChild(); el; el = el->next()) { if (el->childs()) { if (allNumericChildren(el)) { f << ipad << "-\n"; emitSequence(el, ipad); } else { f << innerPad << " " << c->getKey() << ": " << quoteValue(val) << "\n"; } emitSeqElement(el->getChild(), ipad); } } else if (el->getElements() > 1) { for (size_t i = 0; i < el->getElements(); ++i) f << ipad << "- " << quoteValue(getValueAt(el, i)) << "\n"; } else { f << ipad << "- " << quoteValue(getValueAt(el, 0)) << "\n"; } } else if (node->childs()) { // Mapping node with children f << pad << node->getKey() << ":\n"; writeNode(node->getChild(), depth + 1); } else if (isSimpleSequence(node)) { // Simple sequence (list of scalars) f << pad << node->getKey() << ":\n"; std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < node->getElements(); ++i) { f << innerPad << "- " << quoteValue(getValueAt(node, i)) << "\n"; } }; // Emit a mapping that is one element of a sequence: the first key line is // prefixed with "- ", subsequent keys align with two spaces. emitSeqElement = [&](const Config::ConfigData *first, const std::string &pad) { bool firstKey = true; for (const Config::ConfigData *n = first; n; n = n->next()) { std::string marker = firstKey ? "- " : " "; if (n->childs()) { f << pad << marker << n->getKey() << ":\n"; if (allNumericChildren(n)) emitSequence(n, pad + " "); else emitMapping(n->getChild(), pad + " "); } else if (n->getElements() > 1) { f << pad << marker << n->getKey() << ":\n"; for (size_t i = 0; i < n->getElements(); ++i) f << pad << " - " << quoteValue(getValueAt(n, i)) << "\n"; } else { // Simple scalar value f << pad << node->getKey() << ": " << quoteValue(getValueAt(node, 0)) << "\n"; f << pad << marker << n->getKey() << ": " << quoteValue(getValueAt(n, 0)) << "\n"; } node = node->next(); firstKey = false; } }; // Add blank lines between top-level sections for readability // Top-level: blank lines between sections for readability const Config::ConfigData *top = conf->first(); bool firstTop = true; while (top) { for (const Config::ConfigData *n = top; n; n = n->next()) { if (!firstTop) f << "\n"; firstTop = false; if (top->childs()) { f << top->getKey() << ":\n"; writeNode(top->getChild(), 1); } else if (isSimpleSequence(top)) { f << top->getKey() << ":\n"; for (size_t i = 0; i < top->getElements(); ++i) { f << " - " << quoteValue(getValueAt(top, i)) << "\n"; } if (n->childs()) { f << n->getKey() << ":\n"; if (allNumericChildren(n)) emitSequence(n, ""); else emitMapping(n->getChild(), " "); } else if (n->getElements() > 1) { f << n->getKey() << ":\n"; for (size_t i = 0; i < n->getElements(); ++i) f << " - " << quoteValue(getValueAt(n, i)) << "\n"; } else { f << top->getKey() << ": " << quoteValue(getValueAt(top, 0)) << "\n"; f << n->getKey() << ": " << quoteValue(getValueAt(n, 0)) << "\n"; } top = top->next(); } f.close(); Loading @@ -212,7 +213,7 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ void confplus::Yaml::loadConfig(const char* path, Config* conf) { FILE* fh = fopen(path, "rb"); yaml_event_type_t type; yaml_event_type_t type = YAML_NO_EVENT; yaml_parser_t parse; /* Initialize parser */ Loading @@ -227,134 +228,92 @@ void confplus::Yaml::loadConfig(const char* path, Config* conf) { /* Set input file */ yaml_parser_set_input_file(&parse, fh); std::unique_ptr<YamlStack> ystack = nullptr; // Stack-based parser. Each frame is either a mapping or a sequence. // Paths are built from the stack; sequence elements get a numeric index // segment, e.g. /BLOGI/DOMAINS/0/NAMES/1. This produces a clean, fully // indexed tree that supports arbitrary nesting (sequences inside mappings // inside sequences, etc.). struct Frame { bool isSeq; std::string path; // path of this container std::string key; // pending key (mapping only) bool keySet; // whether a key is waiting for its value int seqIndex; // next index (sequence only) }; std::vector<Frame> st; // Compute the path for a new child container under `top`, consuming the // pending mapping key or advancing the sequence index. auto childPath = [](Frame &top) -> std::string { if (top.isSeq) { std::string p = top.path + "/" + std::to_string(top.seqIndex); ++top.seqIndex; return p; } std::string p = top.path + "/" + top.key; top.key.clear(); top.keySet = false; return p; }; bool seq = false; bool seqMapping = false; bool gotValue = false; int seqIndex = 0; std::string key, value; int pos = 0; do { yaml_event_t event; if (!yaml_parser_parse(&parse, &event)) break; type = event.type; switch (event.type) { case YAML_MAPPING_START_EVENT: { if (seq && !seqMapping) { // First mapping inside a sequence: sequence-of-mappings mode seqMapping = true; seqIndex = 0; std::unique_ptr<YamlStack> cystack(new YamlStack); cystack->prevel = std::move(ystack); ystack = std::move(cystack); ystack->anchor = key; key.clear(); value.clear(); } else if (seqMapping) { // Subsequent mapping in the sequence key.clear(); value.clear(); } else { std::unique_ptr<YamlStack> cystack(new YamlStack); cystack->prevel = std::move(ystack); ystack = std::move(cystack); ystack->anchor = key; key = value; value.clear(); } std::string p = st.empty() ? std::string("") : childPath(st.back()); st.push_back(Frame{false, p, "", false, 0}); } break; case YAML_MAPPING_END_EVENT: { if (seqMapping) { // Don't pop stack; increment index for next mapping ++seqIndex; key.clear(); value.clear(); } else if (ystack && ystack->prevel) { ystack = std::move(ystack->prevel); } }break; case YAML_SCALAR_EVENT: { if (key.empty() && !gotValue) std::copy(event.data.scalar.value, event.data.scalar.value + event.data.scalar.length, std::inserter<std::string>(key, std::begin(key))); else { std::copy(event.data.scalar.value, event.data.scalar.value + event.data.scalar.length, std::inserter<std::string>(value, std::begin(value))); gotValue = true; } if (!st.empty()) st.pop_back(); } break; case YAML_SEQUENCE_START_EVENT: { seq = true; std::string p = st.empty() ? std::string("") : childPath(st.back()); st.push_back(Frame{true, p, "", false, 0}); } break; case YAML_SEQUENCE_END_EVENT: { if (seqMapping) { // Pop the stack that was pushed for the first mapping if (ystack && ystack->prevel) { ystack = std::move(ystack->prevel); if (!st.empty()) st.pop_back(); } break; case YAML_SCALAR_EVENT: { std::string s(reinterpret_cast<const char*>(event.data.scalar.value), event.data.scalar.length); if (st.empty()) break; Frame &top = st.back(); if (top.isSeq) { // Scalar element of a sequence -> store at the sequence path // with the running index as positional value. if (!top.path.empty()) { Config::ConfigData *k = conf->setKey(top.path); conf->setValue(k, top.seqIndex, s); } ++top.seqIndex; } else if (!top.keySet) { // Mapping key top.key = s; top.keySet = true; } else { // Mapping value std::string kp = top.path + "/" + top.key; if (kp.size() > 1) { Config::ConfigData *k = conf->setKey(kp); conf->setValue(k, 0, s); } seqMapping = false; seqIndex = 0; top.key.clear(); top.keySet = false; } seq = false; key = value; pos = 0; value.clear(); } break; default: break; } if (!key.empty() && gotValue) { std::vector<std::string> path_elements; for (YamlStack* curst = ystack.get(); curst; curst = curst->prevel.get()) { path_elements.push_back(curst->anchor); } std::string cname = ""; for (int i = path_elements.size() - 1; i >= 0; --i) { if (!path_elements[i].empty()) { cname += "/"; cname += path_elements[i]; } } if (!key.empty()) { cname += "/"; cname += key; } if (cname.empty()) { cname = "/"; cname += key; } Config::ConfigData* ckey = conf->setKey(cname); if (seqMapping) { conf->setValue(ckey, seqIndex, value); value.clear(); key.clear(); gotValue = false; } else { conf->setValue(ckey, pos, value); value.clear(); gotValue = false; if (!seq) { key.clear(); } else { ++pos; } } } type = event.type; yaml_event_delete(&event); } while (type != YAML_STREAM_END_EVENT); Loading
src/conf.cpp +10 −2 Original line number Diff line number Diff line Loading @@ -43,8 +43,16 @@ confplus::Config::ConfigValue::ConfigValue(std::string value,size_t pos){ } confplus::Config::ConfigData* confplus::Config::getKey(const std::string& key) const { ConfigData* cdat = firstData.get(); if (key[0] != '/') { return getKey(key, nullptr); } confplus::Config::ConfigData* confplus::Config::getKey(const std::string& key, const ConfigData* base) const { // base == nullptr -> absolute lookup starting at the document root. // base != nullptr -> relative lookup starting at base's children, so a // path like "/NAMES/1" resolves underneath that node. ConfigData* cdat = base ? const_cast<ConfigData*>(base->Child.get()) : firstData.get(); if (key.empty() || key[0] != '/') { ConfException exp; exp[ConfException::Error] << "Config: getkey wrong path!"; throw exp; Loading
src/conf.h +1 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ namespace confplus { ConfigData *getKey(const std::string &key) const; ConfigData *getKey(const std::string &key, const ConfigData *base) const; ConfigData *setKey(const std::string &key); void delKey(ConfigData *key); Loading
tools/dumptree.cpp 0 → 100644 +32 −0 Original line number Diff line number Diff line #include <iostream> #include <string> #include "conf.h" #include "exception.h" static void dump(const confplus::Config &conf, const confplus::Config::ConfigData *node, const std::string &path){ for(const confplus::Config::ConfigData *cur=node; cur; cur=cur->next()){ std::string p = path + "/" + cur->getKey(); if(cur->childs()){ std::cout << p << " (path, elements=" << cur->getElements() << ")\n"; dump(conf, cur->getChild(), p); }else{ std::cout << p << " (leaf, elements=" << cur->getElements() << ") = ["; for(size_t i=0;i<cur->getElements();++i){ try{ std::cout << (i?", ":"") << conf.getValue(cur,i); }catch(...){} } std::cout << "]\n"; } } } int main(int argc,char *argv[]){ if(argc<2){ std::cerr << "usage: dumptree <backend:path>\n"; return 1; } try{ confplus::Config conf(argv[1]); dump(conf, conf.first(), ""); }catch(confplus::ConfException &e){ std::cerr << e.what() << std::endl; return 1; } return 0; }