Loading backends/yaml/yamlconf.cpp +26 −46 Original line number Diff line number Diff line Loading @@ -60,12 +60,11 @@ const char* confplus::Yaml::getAuthor(){ void confplus::Yaml::saveConfig(const char *path, const Config *conf){ std::ofstream f(path); if (!f.is_open()) { throw "Failed to open file for writing!"; confplus::ConfException err; err[confplus::ConfException::Critical] << "Failed to open file for writing: " << path; throw err; } // 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; Loading @@ -79,7 +78,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ return true; }; // Get value at position from a ConfigData node auto getValueAt = [&conf](const Config::ConfigData *node, size_t pos) -> std::string { try { return conf->getValue(node, pos); Loading @@ -88,12 +86,10 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // Check if a string needs quoting in YAML auto needsQuote = [](const std::string &s) -> bool { if (s.empty()) return true; if (s == "true" || s == "false" || s == "yes" || s == "no" || s == "null" || s == "~") return true; // Check if it's a number bool isNum = true; for (size_t i = 0; i < s.size(); ++i) { char c = s[i]; Loading @@ -101,8 +97,7 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ if (c == '.') continue; if (c < '0' || c > '9') { isNum = false; break; } } if (isNum && !s.empty() && s != "-" && s != "+") return false; // numbers don't need quotes // Check for special characters if (isNum && !s.empty() && s != "-" && s != "+") return false; for (char c : s) { if (c == ':' || c == '#' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == '&' || Loading @@ -123,7 +118,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ 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()) { Loading @@ -133,7 +127,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ 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"; Loading @@ -143,7 +136,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // 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()) { Loading @@ -163,8 +155,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // 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()) { Loading @@ -186,7 +176,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // Top-level: blank lines between sections for readability const Config::ConfigData *top = conf->first(); bool firstTop = true; for (const Config::ConfigData *n = top; n; n = n->next()) { Loading @@ -213,37 +202,33 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ void confplus::Yaml::loadConfig(const char* path, Config* conf) { FILE* fh = fopen(path, "rb"); if (fh == nullptr) { confplus::ConfException err; err[confplus::ConfException::Critical] << "Failed to open file: " << path; throw err; } yaml_event_type_t type = YAML_NO_EVENT; yaml_parser_t parse; /* Initialize parser */ if (fh == nullptr) { throw "Failed to open file!"; if (!yaml_parser_initialize(&parse)) { fclose(fh); confplus::ConfException err; err[confplus::ConfException::Critical] << "Failed to initialize yaml parser!"; throw err; } if (!yaml_parser_initialize(&parse)) throw "Failed to initialize parser!"; /* Set input file */ yaml_parser_set_input_file(&parse, fh); // 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::string path; std::string key; bool keySet; int seqIndex; }; 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); Loading Loading @@ -287,19 +272,15 @@ void confplus::Yaml::loadConfig(const char* path, Config* conf) { 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); Loading @@ -320,4 +301,3 @@ void confplus::Yaml::loadConfig(const char* path, Config* conf) { yaml_parser_delete(&parse); fclose(fh); } No newline at end of file Loading
backends/yaml/yamlconf.cpp +26 −46 Original line number Diff line number Diff line Loading @@ -60,12 +60,11 @@ const char* confplus::Yaml::getAuthor(){ void confplus::Yaml::saveConfig(const char *path, const Config *conf){ std::ofstream f(path); if (!f.is_open()) { throw "Failed to open file for writing!"; confplus::ConfException err; err[confplus::ConfException::Critical] << "Failed to open file for writing: " << path; throw err; } // 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; Loading @@ -79,7 +78,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ return true; }; // Get value at position from a ConfigData node auto getValueAt = [&conf](const Config::ConfigData *node, size_t pos) -> std::string { try { return conf->getValue(node, pos); Loading @@ -88,12 +86,10 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // Check if a string needs quoting in YAML auto needsQuote = [](const std::string &s) -> bool { if (s.empty()) return true; if (s == "true" || s == "false" || s == "yes" || s == "no" || s == "null" || s == "~") return true; // Check if it's a number bool isNum = true; for (size_t i = 0; i < s.size(); ++i) { char c = s[i]; Loading @@ -101,8 +97,7 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ if (c == '.') continue; if (c < '0' || c > '9') { isNum = false; break; } } if (isNum && !s.empty() && s != "-" && s != "+") return false; // numbers don't need quotes // Check for special characters if (isNum && !s.empty() && s != "-" && s != "+") return false; for (char c : s) { if (c == ':' || c == '#' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == '&' || Loading @@ -123,7 +118,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ 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()) { Loading @@ -133,7 +127,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ 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"; Loading @@ -143,7 +136,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // 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()) { Loading @@ -163,8 +155,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // 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()) { Loading @@ -186,7 +176,6 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ } }; // Top-level: blank lines between sections for readability const Config::ConfigData *top = conf->first(); bool firstTop = true; for (const Config::ConfigData *n = top; n; n = n->next()) { Loading @@ -213,37 +202,33 @@ void confplus::Yaml::saveConfig(const char *path,const Config *conf){ void confplus::Yaml::loadConfig(const char* path, Config* conf) { FILE* fh = fopen(path, "rb"); if (fh == nullptr) { confplus::ConfException err; err[confplus::ConfException::Critical] << "Failed to open file: " << path; throw err; } yaml_event_type_t type = YAML_NO_EVENT; yaml_parser_t parse; /* Initialize parser */ if (fh == nullptr) { throw "Failed to open file!"; if (!yaml_parser_initialize(&parse)) { fclose(fh); confplus::ConfException err; err[confplus::ConfException::Critical] << "Failed to initialize yaml parser!"; throw err; } if (!yaml_parser_initialize(&parse)) throw "Failed to initialize parser!"; /* Set input file */ yaml_parser_set_input_file(&parse, fh); // 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::string path; std::string key; bool keySet; int seqIndex; }; 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); Loading Loading @@ -287,19 +272,15 @@ void confplus::Yaml::loadConfig(const char* path, Config* conf) { 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); Loading @@ -320,4 +301,3 @@ void confplus::Yaml::loadConfig(const char* path, Config* conf) { yaml_parser_delete(&parse); fclose(fh); } No newline at end of file