/* * Copyright (c) 2015, Vsevolod Stakhov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include #include #include "ucl.h" // C++11 API inspired by json11: https://github.com/dropbox/json11/ namespace ucl { struct ucl_map_construct_t {}; constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t(); struct ucl_array_construct_t {}; constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t(); class Ucl final { private: struct ucl_deleter { void operator()(ucl_object_t *obj) { ucl_object_unref(obj); } }; static int append_char(unsigned char c, size_t nchars, void *ud) { std::string *out = reinterpret_cast(ud); out->append(nchars, (char) c); return nchars; } static int append_len(unsigned const char *str, size_t len, void *ud) { std::string *out = reinterpret_cast(ud); out->append((const char *) str, len); return len; } static int append_int(int64_t elt, void *ud) { std::string *out = reinterpret_cast(ud); auto nstr = std::to_string(elt); out->append(nstr); return nstr.size(); } static int append_double(double elt, void *ud) { std::string *out = reinterpret_cast(ud); auto nstr = std::to_string(elt); out->append(nstr); return nstr.size(); } static struct ucl_emitter_functions default_emit_funcs() { struct ucl_emitter_functions func = { Ucl::append_char, Ucl::append_len, Ucl::append_int, Ucl::append_double, nullptr, nullptr}; return func; }; static bool ucl_variable_getter(const unsigned char *data, size_t len, unsigned char ** /*replace*/, size_t * /*replace_len*/, bool *need_free, void *ud) { *need_free = false; auto vars = reinterpret_cast *>(ud); if (vars && data && len != 0) { vars->emplace(data, data + len); } return false; } static bool ucl_variable_replacer(const unsigned char *data, size_t len, unsigned char **replace, size_t *replace_len, bool *need_free, void *ud) { *need_free = false; auto replacer = reinterpret_cast(ud); if (!replacer) { return false; } std::string var_name(data, data + len); if (!replacer->is_variable(var_name)) { return false; } std::string var_value = replacer->replace(var_name); if (var_value.empty()) { return false; } *replace = (unsigned char *) UCL_ALLOC(var_value.size()); memcpy(*replace, var_value.data(), var_value.size()); *replace_len = var_value.size(); *need_free = true; return true; } template static Ucl parse_with_strategy_function(C config_func, P parse_func, std::string &err) { auto parser = ucl_parser_new(UCL_PARSER_DEFAULT); config_func(parser); if (!parse_func(parser)) { const char *error = ucl_parser_get_error(parser);//Assigning here without checking result first causes a if (error != NULL) err.assign(error); // crash if ucl_parser_get_error returns NULL ucl_parser_free(parser); return nullptr; } auto obj = ucl_parser_get_object(parser); ucl_parser_free(parser); // Obj will handle ownership return Ucl(obj); } std::unique_ptr obj; public: struct macro_handler_s { ucl_macro_handler handler; ucl_context_macro_handler ctx_handler; }; struct macro_userdata_s { ucl_parser *parser; void *userdata; }; class const_iterator { private: struct ucl_iter_deleter { void operator()(ucl_object_iter_t it) { ucl_object_iterate_free(it); } }; std::shared_ptr it; std::unique_ptr cur; public: typedef std::forward_iterator_tag iterator_category; const_iterator(const Ucl &obj) { it = std::shared_ptr(ucl_object_iterate_new(obj.obj.get()), ucl_iter_deleter()); cur.reset(new Ucl(ucl_object_iterate_safe(it.get(), true))); if (!cur->obj) { it.reset(); cur.reset(); } } const_iterator() { } const_iterator(const const_iterator &other) = delete; const_iterator(const_iterator &&other) = default; ~const_iterator() { } const_iterator &operator=(const const_iterator &other) = delete; const_iterator &operator=(const_iterator &&other) = default; bool operator==(const const_iterator &other) const { if (cur && other.cur) { return cur->obj.get() == other.cur->obj.get(); } return !cur && !other.cur; } bool operator!=(const const_iterator &other) const { return !(*this == other); } const_iterator &operator++() { if (it) { cur.reset(new Ucl(ucl_object_iterate_safe(it.get(), true))); } if (cur && !cur->obj) { it.reset(); cur.reset(); } return *this; } const Ucl &operator*() const { return *cur; } const Ucl *operator->() const { return cur.get(); } }; struct variable_replacer { virtual ~variable_replacer() { } virtual bool is_variable(const std::string &str) const { return !str.empty(); } virtual std::string replace(const std::string &var) const = 0; }; // We grab ownership if get non-const ucl_object_t Ucl(ucl_object_t *other) { obj.reset(other); } // Shared ownership Ucl(const ucl_object_t *other) { obj.reset(ucl_object_ref(other)); } Ucl(const Ucl &other) { obj.reset(ucl_object_ref(other.obj.get())); } Ucl(Ucl &&other) { obj.swap(other.obj); } Ucl() noexcept { obj.reset(ucl_object_typed_new(UCL_NULL)); } Ucl(std::nullptr_t) noexcept { obj.reset(ucl_object_typed_new(UCL_NULL)); } Ucl(double value) { obj.reset(ucl_object_typed_new(UCL_FLOAT)); obj->value.dv = value; } Ucl(int64_t value) { obj.reset(ucl_object_typed_new(UCL_INT)); obj->value.iv = value; } Ucl(bool value) { obj.reset(ucl_object_typed_new(UCL_BOOLEAN)); obj->value.iv = static_cast(value); } Ucl(const std::string &value) { obj.reset(ucl_object_fromstring_common(value.data(), value.size(), UCL_STRING_RAW)); } Ucl(const char *value) { obj.reset(ucl_object_fromstring_common(value, 0, UCL_STRING_RAW)); } // Implicit constructor: anything with a to_json() function. template Ucl(const T &t) : Ucl(t.to_ucl()) { } // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) template::value && std::is_constructible::value, int>::type = 0> Ucl(const M &m) { obj.reset(ucl_object_typed_new(UCL_OBJECT)); auto cobj = obj.get(); for (const auto &e: m) { ucl_object_insert_key(cobj, ucl_object_ref(e.second.obj.get()), e.first.data(), e.first.size(), true); } } // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) template::value, int>::type = 0> Ucl(const V &v) { obj.reset(ucl_object_typed_new(UCL_ARRAY)); auto cobj = obj.get(); for (const auto &e: v) { ucl_array_append(cobj, ucl_object_ref(e.obj.get())); } } ucl_type_t type() const { if (obj) { return ucl_object_type(obj.get()); } return UCL_NULL; } std::string key() const { std::string res; if (obj->key) { res.assign(obj->key, obj->keylen); } return res; } double number_value(const double default_val = 0.0) const { double res; if (ucl_object_todouble_safe(obj.get(), &res)) { return res; } return default_val; } int64_t int_value(const int64_t default_val = 0) const { int64_t res; if (ucl_object_toint_safe(obj.get(), &res)) { return res; } return default_val; } bool bool_value(const bool default_val = false) const { bool res; if (ucl_object_toboolean_safe(obj.get(), &res)) { return res; } return default_val; } std::string string_value(const std::string &default_val = "") const { const char *res = nullptr; if (ucl_object_tostring_safe(obj.get(), &res)) { return res; } return default_val; } std::string forced_string_value() const { return ucl_object_tostring_forced(obj.get()); } size_t size() const { if (type() == UCL_ARRAY) { return ucl_array_size(obj.get()); } return 0; } Ucl at(size_t i) const { if (type() == UCL_ARRAY) { return Ucl(ucl_array_find_index(obj.get(), i)); } return Ucl(nullptr); } Ucl lookup(const std::string &key) const { if (type() == UCL_OBJECT) { return Ucl(ucl_object_lookup_len(obj.get(), key.data(), key.size())); } return Ucl(nullptr); } inline Ucl operator[](size_t i) const { return at(i); } inline Ucl operator[](const std::string &key) const { return lookup(key); } // Serialize. void dump(std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const { struct ucl_emitter_functions cbdata; cbdata = Ucl::default_emit_funcs(); cbdata.ud = reinterpret_cast(&out); ucl_object_emit_full(obj.get(), type, &cbdata, nullptr); } std::string dump(ucl_emitter_t type = UCL_EMIT_JSON) const { std::string out; dump(out, type); return out; } static Ucl parse(const std::string &in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { return parse(in, std::map(), err, duplicate_strategy); } static Ucl parse(const std::string &in, const std::map &vars, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { std::vector> emptyVector; return parse(in, vars, emptyVector, err, duplicate_strategy); } //Macro handler will receive a macro_userdata_s as void *ud static Ucl parse(const std::string &in, std::vector> ¯os, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { return parse(in, std::map(), macros, err, duplicate_strategy); } //Macro handler will receive a macro_userdata_s as void *ud static Ucl parse(const std::string &in, const std::map &vars, std::vector> ¯os, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { //Preserve macro_userdata_s memory for later use in parse_with_strategy_function() std::vector userdata_list; userdata_list.reserve(macros.size()); auto config_func = [&userdata_list, &vars, ¯os](ucl_parser *parser) { for (const auto &item: vars) { ucl_parser_register_variable(parser, item.first.c_str(), item.second.c_str()); } for (auto ¯o: macros) { userdata_list.push_back({parser, std::get<2>(macro)}); if (std::get<1>(macro).handler != NULL) { ucl_parser_register_macro(parser, std::get<0>(macro).c_str(), std::get<1>(macro).handler, reinterpret_cast(&userdata_list.back())); } else if (std::get<1>(macro).ctx_handler != NULL) { ucl_parser_register_context_macro(parser, std::get<0>(macro).c_str(), std::get<1>(macro).ctx_handler, reinterpret_cast(&userdata_list.back())); } } }; auto parse_func = [&in, &duplicate_strategy](struct ucl_parser *parser) -> bool { return ucl_parser_add_chunk_full(parser, (unsigned char *) in.data(), in.size(), (unsigned int) ucl_parser_get_default_priority(parser), duplicate_strategy, UCL_PARSE_UCL); }; return parse_with_strategy_function(config_func, parse_func, err); } static Ucl parse(const std::string &in, const variable_replacer &replacer, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { std::vector> emptyVector; return parse(in, replacer, emptyVector, err, duplicate_strategy); } //Macro handler will receive a macro_userdata_s as void *ud static Ucl parse(const std::string &in, const variable_replacer &replacer, std::vector> ¯os, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { //Preserve macro_userdata_s memory for later use in parse_with_strategy_function() std::vector userdata_list; userdata_list.reserve(macros.size()); auto config_func = [&userdata_list, &replacer, ¯os](ucl_parser *parser) { ucl_parser_set_variables_handler(parser, ucl_variable_replacer, &const_cast(replacer)); for (auto ¯o: macros) { userdata_list.push_back({parser, std::get<2>(macro)}); if (std::get<1>(macro).handler != NULL) { ucl_parser_register_macro(parser, std::get<0>(macro).c_str(), std::get<1>(macro).handler, reinterpret_cast(&userdata_list.back())); } else if (std::get<1>(macro).ctx_handler != NULL) { ucl_parser_register_context_macro(parser, std::get<0>(macro).c_str(), std::get<1>(macro).ctx_handler, reinterpret_cast(&userdata_list.back())); } } }; auto parse_func = [&in, &duplicate_strategy](struct ucl_parser *parser) -> bool { return ucl_parser_add_chunk_full(parser, (unsigned char *) in.data(), in.size(), (unsigned int) ucl_parser_get_default_priority(parser), duplicate_strategy, UCL_PARSE_UCL); }; return parse_with_strategy_function(config_func, parse_func, err); } static Ucl parse(const char *in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { return parse(in, std::map(), err, duplicate_strategy); } static Ucl parse(const char *in, const std::map &vars, std::string &err) { if (!in) { err = "null input"; return nullptr; } return parse(std::string(in), vars, err); } //Macro handler will receive a macro_userdata_s as void *ud static Ucl parse(const char *in, std::vector> ¯os, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { return parse(in, std::map(), macros, err, duplicate_strategy); } //Macro handler will receive a macro_userdata_s as void *ud static Ucl parse(const char *in, const std::map &vars, std::vector> ¯os, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { if (!in) { err = "null input"; return nullptr; } return parse(std::string(in), vars, macros, err, duplicate_strategy); } static Ucl parse(const char *in, const variable_replacer &replacer, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { if (!in) { err = "null input"; return nullptr; } return parse(std::string(in), replacer, err, duplicate_strategy); } //Macro handler will receive a macro_userdata_s as void *ud static Ucl parse(const char *in, const variable_replacer &replacer, std::vector> ¯os, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) { if (!in) { err = "null input"; return nullptr; } return parse(std::string(in), replacer, macros, err, duplicate_strategy); } static Ucl parse_from_file(const std::string &filename, std::string &err) { return parse_from_file(filename, std::map(), err); } static Ucl parse_from_file(const std::string &filename, const std::map &vars, std::string &err) { auto config_func = [&vars](ucl_parser *parser) { for (const auto &item: vars) { ucl_parser_register_variable(parser, item.first.c_str(), item.second.c_str()); } }; auto parse_func = [&filename](ucl_parser *parser) { return ucl_parser_add_file(parser, filename.c_str()); }; return parse_with_strategy_function(config_func, parse_func, err); } static Ucl parse_from_file(const std::string &filename, const variable_replacer &replacer, std::string &err) { auto config_func = [&replacer](ucl_parser *parser) { ucl_parser_set_variables_handler(parser, ucl_variable_replacer, &const_cast(replacer)); }; auto parse_func = [&filename](ucl_parser *parser) { return ucl_parser_add_file(parser, filename.c_str()); }; return parse_with_strategy_function(config_func, parse_func, err); } static std::vector find_variable(const std::string &in) { auto parser = ucl_parser_new(UCL_PARSER_DEFAULT); std::set vars; ucl_parser_set_variables_handler(parser, ucl_variable_getter, &vars); ucl_parser_add_chunk(parser, (const unsigned char *) in.data(), in.size()); ucl_parser_free(parser); std::vector result; std::move(vars.begin(), vars.end(), std::back_inserter(result)); return result; } static std::vector find_variable(const char *in) { if (!in) { return std::vector(); } return find_variable(std::string(in)); } static std::vector find_variable_from_file(const std::string &filename) { auto parser = ucl_parser_new(UCL_PARSER_DEFAULT); std::set vars; ucl_parser_set_variables_handler(parser, ucl_variable_getter, &vars); ucl_parser_add_file(parser, filename.c_str()); ucl_parser_free(parser); std::vector result; std::move(vars.begin(), vars.end(), std::back_inserter(result)); return result; } Ucl &operator=(Ucl rhs) { obj.swap(rhs.obj); return *this; } bool operator==(const Ucl &rhs) const { return ucl_object_compare(obj.get(), rhs.obj.get()) == 0; } bool operator<(const Ucl &rhs) const { return ucl_object_compare(obj.get(), rhs.obj.get()) < 0; } bool operator!=(const Ucl &rhs) const { return !(*this == rhs); } bool operator<=(const Ucl &rhs) const { return !(rhs < *this); } bool operator>(const Ucl &rhs) const { return (rhs < *this); } bool operator>=(const Ucl &rhs) const { return !(*this < rhs); } explicit operator bool() const { if (!obj || type() == UCL_NULL) { return false; } if (type() == UCL_BOOLEAN) { return bool_value(); } return true; } const_iterator begin() const { return const_iterator(*this); } const_iterator cbegin() const { return const_iterator(*this); } const_iterator end() const { return const_iterator(); } const_iterator cend() const { return const_iterator(); } }; };// namespace ucl