/* * This file is a part of PikoTools * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2021, Tomasz Sowa * 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. * * * Neither the name Tomasz Sowa nor the names of contributors to this * project may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. */ #ifndef headerfile_picotools_space_space #define headerfile_picotools_space_space #include #include #include #include #include #include "textstream/types.h" #include "convert/inttostr.h" //#include "utf8/utf8.h" namespace PT { /* A parser for parsing config files. A config file can look like this: variable1 = value 1 variable2 = " value 2 " variable3 = (value 1, value 2) variable4 = (" value 1 " , "value2", value 3) sample of use: SpaceParser parser; parser.Parse("/path/to/config"); if( parser.status == SpaceParser::ok ) { // the whole config we have in parser.table } config syntax: option = list list can consists of any number of items, if you're using more than one item you should use brackets () for one item the brackets can be ommited: option = value white characters at the beginning of the value (and at the end) will be trimmed, or you can use quotes: option = "value" option2 = "value with spaces at the end " the form without quotes: option = value should be written in one line, so this is not allowed: option = value you can use a new line characters only between brackets and quotes: option = "this is a multiline string" option = ( value1, value2 ) but there is one requirement: the first character " or ( should be in the same line, so this is not allowed option = "this is wrong" but this is ok: option = " that is ok" empty lists: option = () this creates an empty list: parser.table['option'].empty() == true option = this creates an empty list too (the same as previously) option = "" but this doesn't create an empty list, it creates a list with one (empty) item commentaries: # this is a commentary (until the end of the line) option = value # this is a commentary too commentaries are treated as white characters, other example: option = ( # this is my list "value 1" # this is a value one value 2 # and this is a value two ) # end of my list overwriting: option1 = some value option1 = other value # always the last option is used so option1 is "other value" list delimiter: option1 = (value1, value2, value3) option2 = ("value1", "value2", "value3") above we're using a comma ',' as a list delimiter but when using quotes (second line) the commas can be omitted: option2 = ("value1" "value2" "value3") white characters: the name of an option cannot consist of white characters some option = value # this is wrong some_option = value # this is ok which characters are allowed in an option name is defined by IsVariableChar() method you can use white characters in values option = value with spaces or tabs white characters at the beginning and at the end will be trimmed, so if you want them use quotes: option = " other value with spaces " special characters in quoted strings: option = "this is a string with \" a quote inside" the option will be: this is a string with " a quote inside \\ - means one \ basically: \char produces char so: "\a" gives "a" "\\" gives "\" "\Z" gives "Z" and so on you can call UseEscapeChar(false) to turn this off */ class Space { public: typedef std::map ObjectType; typedef std::vector TableType; enum Escape { no_escape, escape_space, escape_json, }; enum Type { type_null, type_bool, type_long, type_float, type_double, type_string, type_wstring, type_object, type_table, }; union Value { bool value_bool; long long value_long; float value_float; double value_double; std::string value_string; std::wstring value_wstring; ObjectType value_object; TableType value_table; Value() { } ~Value() { } }; Type type; Value value; Space(); Space(const Space & space); Space & operator=(const Space & space); // add move cctor ~Space(); Space(bool val); Space(short val); Space(int val); Space(long val); Space(long long val); Space(unsigned short val); Space(unsigned int val); Space(unsigned long val); Space(unsigned long long val); Space(float val); Space(double val); Space(const char * str); Space(const wchar_t * str); Space(const std::string & str); Space(const std::wstring & str); Space(const Space * space); // set a new value void set(bool val); void set(short val); void set(int val); void set(long val); void set(long long val); void set(unsigned short val); void set(unsigned int val); void set(unsigned long val); void set(unsigned long long val); void set(float val); void set(double val); void set(const char * str); void set(const wchar_t * str); void set(const std::string & str); void set(const std::wstring & str); void set(const Space * space = nullptr); // add a value to the table, change to table if needed, return the reference to the new inserted item Space & add(bool val); Space & add(short val); Space & add(int val); Space & add(long val); Space & add(long long val); Space & add(unsigned short val); Space & add(unsigned int val); Space & add(unsigned long val); Space & add(unsigned long long val); Space & add(float val); Space & add(double val); Space & add(const char * val); Space & add(const wchar_t * val); Space & add(const std::string & val); Space & add(const std::wstring & val); Space & add(const Space * space); // add a value to the object, change to object if needed, return the reference to the new inserted item Space & add(const wchar_t * field, bool val); Space & add(const wchar_t * field, short val); Space & add(const wchar_t * field, int val); Space & add(const wchar_t * field, long val); Space & add(const wchar_t * field, long long val); Space & add(const wchar_t * field, unsigned short val); Space & add(const wchar_t * field, unsigned int val); Space & add(const wchar_t * field, unsigned long val); Space & add(const wchar_t * field, unsigned long long val); Space & add(const wchar_t * field, float val); Space & add(const wchar_t * field, double val); Space & add(const wchar_t * field, const char * val); Space & add(const wchar_t * field, const wchar_t * val); Space & add(const wchar_t * field, const std::string & val); Space & add(const wchar_t * field, const std::wstring & val); Space & add(const wchar_t * field, const Space * space); Space & add_empty_space(const wchar_t * field); // IMPROVEME rename me to something better bool is_null() const; bool is_bool() const; bool is_llong() const; bool is_long_long() const; bool is_float() const; bool is_double() const; bool is_numeric() const; bool is_str() const; bool is_wstr() const; bool is_text() const; bool is_object() const; bool is_table() const; bool to_bool() const; short to_short() const; int to_int() const; long to_long() const; long long to_llong() const; long long to_long_long() const; unsigned short to_ushort() const; unsigned int to_uint() const; unsigned long to_ulong() const; unsigned long long to_ullong() const; unsigned long long to_ulong_long() const; std::string to_str() const; std::wstring to_wstr() const; bool * get_bool(); long long * get_llong(); long long * get_long_long(); float * get_float(); double * get_double(); std::string * get_string(); std::wstring * get_wstring(); ObjectType * get_object(); TableType * get_table(); const bool * get_bool() const; const long long * get_llong() const; const long long * get_long_long() const; const float * get_float() const; const double * get_double() const; const std::string * get_string() const; const std::wstring * get_wstring() const; const ObjectType * get_object() const; const TableType * get_table() const; std::string serialize_to_space_str() const; std::wstring serialize_to_space_wstr() const; void serialize_to_space_to(std::string & str) const; void serialize_to_space_to(std::wstring & str) const; template void serialize_to_space_stream(StreamType & str) const { // IMPROVEME switch(type) { case type_null: //serialize_null(str, escape); break; case type_bool: //serialize_bool(str, escape); break; case type_long: //serialize_long(str, escape); break; case type_float: //serialize_float(str, escape); break; case type_double: //serialize_double(str, escape); break; case type_string: //serialize_string(str, escape); break; case type_wstring: //serialize_wstring(str, escape); break; case type_object: //serialize_object(str, escape); break; case type_table: //serialize_table(str, escape); break; } } std::string serialize_to_json_str() const; std::wstring serialize_to_json_wstr() const; void serialize_to_json_to(std::string & str) const; void serialize_to_json_to(std::wstring & str) const; template void serialize_to_json_stream(StreamType & str) const { switch(type) { case type_null: serialize_json_null(str); break; case type_bool: serialize_json_bool(str); break; case type_long: serialize_json_long(str); break; case type_float: serialize_json_float(str); break; case type_double: serialize_json_double(str); break; case type_string: serialize_json_string(str); break; case type_wstring: serialize_json_wstring(str); break; case type_object: serialize_json_object(str); break; case type_table: serialize_json_table(str); break; } } protected: template Space & add_generic(ArgType val) { initialize_value_table_if_needed(); Space * new_space = new Space(val); value.value_table.push_back(new_space); return *value.value_table.back(); } template Space & add_generic(const wchar_t * field, ArgType val) { initialize_value_object_if_needed(); auto insert_res = value.value_object.insert(std::make_pair(field, nullptr)); insert_res.first->second = new Space(val); return *(insert_res.first->second); } template ArgType to_generic_numeric_signed_value() const { long long val = to_long_long(); if( val < std::numeric_limits::min() || val > std::numeric_limits::max() ) val = 0; return val; } template ArgType to_generic_numeric_unsigned_value() const { unsigned long long val = to_ulong_long(); if( val > std::numeric_limits::max() ) val = 0; return val; } long long convert_string_to_long_long() const; long long convert_wstring_to_long_long() const; unsigned long long convert_string_to_ulong_long() const; unsigned long long convert_wstring_to_ulong_long() const; template void escape_to_space_format(int c, StreamType & out) const { // IMPLEMENT ME } template void escape_to_json_format(int c, StreamType & out) const { switch(c) { case 0: out << '\\'; out << '0'; break; case '\r': out << '\\'; out << 'r'; break; case '\n': out << '\\'; out << 'n'; break; case '\\': out << '\\'; out << '\\'; break; case '"': out << '\\'; out << '\"'; break; //case '(': out << '\\'; out << '('; break; //case ')': out << '\\'; out << ')'; break; //case '=': out << '\\'; out << '='; break; default: out << static_cast(c); } } template void copy_input_string_to_output(const CharT * input_str, StreamType & out_str, Escape escape) const { while( *input_str ) { if( escape == Escape::no_escape ) out_str << static_cast(*input_str); else if( escape == Escape::escape_space ) escape_to_space_format(*input_str, out_str); else if( escape == Escape::escape_json ) escape_to_json_format(*input_str, out_str); input_str += 1; } } template void copy_input_stream_to_output(const StreamType & input_str, StreamType & out_str, Escape escape) const { typename StreamType::const_iterator i = input_str.begin(); while( i != input_str.end() ) { if( escape == Escape::no_escape ) out_str << static_cast(*i); else if( escape == Escape::escape_space ) escape_to_space_format(*i, out_str); else if( escape == Escape::escape_json ) escape_to_json_format(*i, out_str); ++i; } } template void serialize_string_buffer(const char * input_str, StreamType & out_str, Escape escape) const { if constexpr ( sizeof(char) == sizeof(typename StreamType::char_type) ) { // input and output are char (we assume it is utf8) copy_input_string_to_output(input_str, out_str, escape); } else { StreamType temp_stream; // input is utf8 but output is wide UTF8ToWide(input_str, temp_stream, false); copy_input_stream_to_output(temp_stream, out_str, escape); } } template void serialize_string_buffer(const wchar_t * input_str, StreamType & out_str, Escape escape) const { if constexpr ( sizeof(wchar_t) == sizeof(typename StreamType::char_type) ) { // input and output are wide characters copy_input_string_to_output(input_str, out_str, escape); } else { StreamType temp_stream; // input is wide but output is utf8 WideToUTF8(input_str, temp_stream, false); copy_input_stream_to_output(temp_stream, out_str, escape); } } template void serialize_json_null(StreamType & str) const { serialize_string_buffer(L"null", str, Escape::escape_json); } template void serialize_json_bool(StreamType & str) const { if( value.value_bool ) { serialize_string_buffer(L"true", str, Escape::escape_json); } else { serialize_string_buffer(L"false", str, Escape::escape_json); } } template void serialize_json_long(StreamType & str) const { wchar_t buffer[50]; size_t buffer_len = sizeof(buffer) / sizeof(char); PT::Toa(value.value_long, buffer, buffer_len); serialize_string_buffer(buffer, str, Escape::escape_json); } template void serialize_json_float(StreamType & str) const { wchar_t buffer[50]; size_t buffer_len = sizeof(buffer) / sizeof(char); std::swprintf(buffer, buffer_len, L"%f", value.value_float); serialize_string_buffer(buffer, str, Escape::escape_json); } template void serialize_json_double(StreamType & str) const { wchar_t buffer[50]; size_t buffer_len = sizeof(buffer) / sizeof(char); std::swprintf(buffer, buffer_len, L"%f", value.value_double); serialize_string_buffer(buffer, str, Escape::escape_json); } template void serialize_json_string(StreamType & str) const { str << '"'; serialize_string_buffer(value.value_string.c_str(), str, Escape::escape_json); str << '"'; } template void serialize_json_wstring(StreamType & str) const { str << '"'; serialize_string_buffer(value.value_wstring.c_str(), str, Escape::escape_json); str << '"'; } template void serialize_json_object(StreamType & str) const { str << '{'; bool is_first = true; for(auto & map_item : value.value_object) { if( !is_first ) { str << ','; } str << '"'; serialize_string_buffer(map_item.first.c_str(), str, Escape::escape_json); str << '"'; str << ':'; map_item.second->serialize_to_json_stream(str); is_first = false; } str << '}'; } template void serialize_json_table(StreamType & str) const { str << '['; bool is_first = true; for(Space * space : value.value_table) { if( !is_first ) { str << ','; } space->serialize_to_json_stream(str); is_first = false; } str << ']'; } public: ///////////////////////////// remove me template void Serialize(Stream & out, bool use_indents = false, bool use_comments = false, int level = 0) const { } template void SerializeTableMulti(Stream & out, bool use_indents, int level) const { } template static void PrintValue(Stream & out, const StringType & str, bool use_quote = true) { } template static void PrintKey(Stream & out, const std::wstring & str) { } template static void PrintLevel(Stream & out, bool use_indents, int level) { } ///////////////////////////// protected: void copy_value_from(const Space & space); void copy_value_object(const Value & value_from); void copy_value_table(const Value & value_from); void initialize_value_null_if_needed(); void initialize_value_bool_if_needed(); void initialize_value_long_if_needed(); void initialize_value_float_if_needed(); void initialize_value_double_if_needed(); void initialize_value_string_if_needed(); void initialize_value_wstring_if_needed(); void initialize_value_object_if_needed(); void initialize_value_table_if_needed(); void remove_value(); void remove_value_string(); void remove_value_wstring(); void remove_value_object(); void remove_value_table(); }; } // namespace #endif