Commit 502d61a1 authored by jan.koester's avatar jan.koester
Browse files

test

parent 845b1f86
Loading
Loading
Loading
Loading
+151 −192
Original line number Diff line number Diff line
@@ -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(){

}
@@ -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
@@ -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();
@@ -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 */
@@ -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);

+10 −2
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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);

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;
}