- W12.1 context_plugin (engineer-zhou): wrap C ABI surface in try/catch, add OOM-safe strdup_message_fields helper, make g_max_tokens drive message-count trim (option A). - W12.2 config refactor (architect-lin): introduce plugins/config/include/toml_parse.h to eliminate 74-line parser duplication; config_plugin delegates to host->config_get/set, collapsing the dual-store data island; ConfigStore::get() now copies via thread_local std::string to remove c_str() dangling under concurrent set(). Zero ABI changes. - W12.3 CLI command parsing (engineer-zhao): guard /clear and /context on missing session service; refactor /file dispatch so bare /file write hits usage instead of unknown-command. - W12.4 build path unification (devops-hu): set per-target RUNTIME_OUTPUT_DIRECTORY on dstalk-cli; remove stale build/dstalk-cli/dstalk-cli.exe so build/bin/ is the sole binary. - W12.5 STATUS.md auto-refresh (engineer-li): run W11.6 script to regenerate STATUS from live profile/group data. - W12.6 plugin-abi.md (writer-deng): add §8 exception safety across ABI boundary and §9 string return lifetime; reference real audit-found violations as anti-examples. Verified: cmake build 0 error 0 warning, ctest 4/4 pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
68 lines
2.3 KiB
C++
68 lines
2.3 KiB
C++
#pragma once
|
|
// Shared TOML parser — used by both ConfigStore (core) and config plugin.
|
|
// W12.2: Extracted from config_store.cpp:23-61 and config_plugin.cpp:28-66
|
|
// to eliminate the 74-line code duplication (W11.2 audit Finding 1).
|
|
// Does NOT support: inline tables, arrays, multi-line strings, escape sequences.
|
|
|
|
#include <string>
|
|
|
|
namespace dstalk {
|
|
namespace toml {
|
|
|
|
/// Parse a TOML string, calling on_kv(full_key, value) for each key-value pair.
|
|
/// Supports [section] headers, key = "value" pairs, # comments, blank lines.
|
|
template<typename F>
|
|
inline void parse(const std::string& content, F&& on_kv)
|
|
{
|
|
std::string current_section;
|
|
size_t pos = 0;
|
|
|
|
while (pos < content.size()) {
|
|
// Trim left whitespace
|
|
while (pos < content.size() && (content[pos] == ' ' || content[pos] == '\t'))
|
|
pos++;
|
|
if (pos >= content.size()) break;
|
|
|
|
// Extract next line
|
|
size_t nl = content.find('\n', pos);
|
|
std::string line = (nl != std::string::npos)
|
|
? content.substr(pos, nl - pos) : content.substr(pos);
|
|
pos = (nl != std::string::npos) ? nl + 1 : content.size();
|
|
|
|
// Trim right whitespace (including \r)
|
|
while (!line.empty() && (line.back() == '\r' || line.back() == ' '))
|
|
line.pop_back();
|
|
|
|
// Skip empty lines and comments
|
|
if (line.empty() || line[0] == '#') continue;
|
|
|
|
// Section header: [section_name]
|
|
if (line[0] == '[' && line.back() == ']') {
|
|
current_section = line.substr(1, line.size() - 2);
|
|
continue;
|
|
}
|
|
|
|
// Key = value
|
|
size_t eq = line.find('=');
|
|
if (eq == std::string::npos) continue;
|
|
|
|
std::string key = line.substr(0, eq);
|
|
while (!key.empty() && key.back() == ' ') key.pop_back();
|
|
if (key.empty()) continue;
|
|
|
|
std::string val = line.substr(eq + 1);
|
|
while (!val.empty() && (val.front() == ' ' || val.front() == '\t'))
|
|
val.erase(0, 1);
|
|
if (val.size() >= 2 && val.front() == '"' && val.back() == '"')
|
|
val = val.substr(1, val.size() - 2);
|
|
|
|
std::string full_key = current_section.empty()
|
|
? key : current_section + "." + key;
|
|
|
|
on_kv(full_key, val);
|
|
}
|
|
}
|
|
|
|
} // namespace toml
|
|
} // namespace dstalk
|