5 size_t parser::find_first_nonescaped(
const std::string &str,
char ch)
7 size_t result = std::string::npos;
10 for (
size_t i = 0; i < str.length(); ++i) {
14 }
else if (str[i] ==
'\\') {
17 }
else if (str[i] == ch) {
27 size_t parser::find_last_escaped(
const std::string &str,
char ch)
29 size_t result = std::string::npos;
32 for (
size_t i = 0; i < str.length(); ++i) {
40 }
else if (str[i] ==
'\\') {
49 std::string parser::unescape(
const std::string &str)
51 std::string result = str;
54 auto it = result.begin();
55 while (it != result.end()) {
59 }
else if (*it ==
'\\') {
62 it = result.erase(it);
73 std::string parser::delete_comment(
const std::string &str)
75 return str.substr(0, find_first_nonescaped(str,
';'));
78 std::vector<std::string> parser::parse_option_list(
const std::string &str)
80 using namespace string_utils;
82 std::string searched = str;
83 std::vector<std::string> result;
86 size_t pos = find_first_nonescaped(searched,
',');
87 if (pos == std::string::npos) {
94 pos = find_first_nonescaped(searched, delim);
97 std::string value = searched.substr(0, pos);
100 size_t whitespace_pos = find_last_escaped(value,
' ');
102 if (whitespace_pos != std::string::npos) {
104 if (value.size() == whitespace_pos) {
105 value.push_back(
' ');
109 value = unescape(value);
112 result.push_back(value);
114 if (pos == std::string::npos) {
118 searched = searched.substr(pos + 1);
124 void parser::handle_links(
125 const config &cfg,
const section &last_section, std::vector<std::string> &option_val_list,
size_t line_number)
127 using namespace string_utils;
129 for (
auto &opt_value : option_val_list) {
131 std::string link = opt_value.substr(2, opt_value.length() - 3);
132 size_t delim = find_first_nonescaped(link,
'#');
136 if (delim == std::string::npos || (delim + 1) == link.length()) {
137 throw parser_exception(
"Bad format of link on line " + std::to_string(line_number));
140 std::string sect_link = link.substr(0, delim);
141 std::string opt_link = link.substr(delim + 1);
143 if (sect_link.empty()) {
144 throw parser_exception(
145 "Section name in link cannot be empty on line " + std::to_string(line_number));
149 const section *selected_section =
nullptr;
150 if (last_section.get_name() == sect_link) {
151 selected_section = &last_section;
152 }
else if (cfg.contains(sect_link)) {
153 selected_section = &cfg[sect_link];
155 throw parser_exception(
"Bad link on line " + std::to_string(line_number));
159 if (selected_section->contains(opt_link)) {
160 opt_value = selected_section->operator[](opt_link).get<string_ini_t>();
162 throw parser_exception(
"Option name in link not found on line " + std::to_string(line_number));
168 void parser::validate_identifier(
const std::string &str,
size_t line_number)
170 std::regex reg_expr(
"^[a-zA-Z.$:][-a-zA-Z0-9_~.:$ ]*$");
171 if (!std::regex_match(str, reg_expr)) {
172 throw parser_exception(
"Identifier contains forbidden characters on line " + std::to_string(line_number));
176 config parser::internal_load(std::istream &str)
178 using namespace string_utils;
181 std::shared_ptr<section> last_section =
nullptr;
183 size_t line_number = 0;
185 while (std::getline(str, line)) {
189 line = delete_comment(line);
198 if (line.length() == 2) {
199 throw parser_exception(
"Section name cannot be empty on line " + std::to_string(line_number));
203 if (last_section !=
nullptr) {
204 cfg.add_section(*last_section);
208 std::string sect_name = unescape(line.substr(1, line.length() - 2));
209 validate_identifier(sect_name, line_number);
210 last_section = std::make_shared<section>(sect_name);
212 throw parser_exception(
"Section not ended on line " + std::to_string(line_number));
215 size_t opt_delim = find_first_nonescaped(line,
'=');
216 if (opt_delim == std::string::npos) {
217 throw parser_exception(
"Unknown element option expected on line " + std::to_string(line_number));
221 if (last_section ==
nullptr) {
222 throw parser_exception(
"Option not in section on line " + std::to_string(line_number));
226 if ((opt_delim + 1) == line.length()) {
227 throw parser_exception(
"Option value cannot be empty on line " + std::to_string(line_number));
231 std::string option_name = unescape(
trim(line.substr(0, opt_delim)));
232 std::string option_val = line.substr(opt_delim + 1);
235 validate_identifier(option_name, line_number);
237 if (option_name.empty()) {
238 throw parser_exception(
"Option name cannot be empty on line " + std::to_string(line_number));
241 auto option_val_list = parse_option_list(option_val);
242 if (option_val_list.empty()) {
243 throw parser_exception(
"Option value cannot be empty on line " + std::to_string(line_number));
246 handle_links(cfg, *last_section, option_val_list, line_number);
249 option opt(option_name, option_val_list);
250 last_section->add_option(opt);
255 if (last_section !=
nullptr) {
256 cfg.add_section(*last_section);
262 void parser::internal_save(
const config &cfg,
const schema &schm, std::ostream &str)
264 for (
auto § : cfg) {
265 bool contains = schm.contains(sect.get_name());
275 auto §_schema = schm[sect.get_name()];
276 sect_schema.write_additional_info(str);
277 sect_schema.write_section_name(str);
280 for (
auto &opt : sect) {
281 if (sect_schema.contains(opt.get_name())) {
283 sect_schema[opt.get_name()].write_additional_info(str);
291 for (
size_t i = 0; i < sect_schema.size(); ++i) {
292 auto &opt_schema = sect_schema[i];
293 if (sect.contains(opt_schema.get_name())) {
307 std::istringstream input(str);
308 return internal_load(input);
313 std::istringstream input(str);
314 config cfg = internal_load(input);
321 return internal_load(str);
326 config cfg = internal_load(str);
333 std::ifstream input(file);
338 return internal_load(input);
343 std::ifstream input(file);
348 config cfg = internal_load(input);
355 std::ofstream output(file);
367 std::ofstream output(file);
368 internal_save(cfg, schm, output);
374 internal_save(cfg, schm, str);
379 std::ofstream output(file);
std::string trim(const std::string &str)
std::string right_trim(const std::string &str)
bool starts_with(const std::string &str, const std::string &search_str)
static config load_file(const std::string &file)
bool ends_with(const std::string &str, const std::string &search_str)
static void save(const config &cfg, const std::string &file)
void validate(const schema &schm, schema_mode mode)
std::string left_trim(const std::string &str)
static config load(const std::string &str)