/* * This file is a part of PikoTools * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2017, 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 "textstream/types.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: /* this is the table which represents your config file in the Table map: the first (key) is your 'option' and the second is 'list' */ typedef std::vector Value; typedef std::map Table; Space(); ~Space(); Space(const Space & s); Space & operator=(const Space & s); // IMPROVE ME // add move cctor void Clear(); /* returns true if such an option has 'value' useful when testing lists (they don't have to be copied out) */ bool HasValue(const wchar_t * name, const wchar_t * value); bool HasValue(const wchar_t * name, const std::wstring & value); bool HasValue(const std::wstring & name, const wchar_t * value); bool HasValue(const std::wstring & name, const std::wstring & value); /* * * methods for getting/finding a value * * */ // moze tu powinno być FindValue? Value * GetValue(const wchar_t * name); Value * GetValue(const std::wstring & name); const Value * GetValue(const wchar_t * name) const; const Value * GetValue(const std::wstring & name) const; // moze tu powinno być FindFirstValue? // they can return a null pointer if there is not such a 'name' std::wstring * GetFirstValue(const wchar_t * name); std::wstring * GetFirstValue(const std::wstring & name); const std::wstring * GetFirstValue(const wchar_t * name) const; const std::wstring * GetFirstValue(const std::wstring & name) const; /* those methods are used to extract information from space.table as a parameter they take the name of an option and a default value (if there is no such a parameter), they return appropriate value (either text, int or boolean) (in lists they return the first item if exists) when calling Text(...) and AText(...) you should copy the object to whom a reference is returned it will be cleared in a next call to one of these methods (as well to Int() Size() and Bool()) AText(...) always returns a reference to UTF-8 string */ std::wstring Text(const wchar_t * name) const; std::wstring Text(const wchar_t * name, const wchar_t * def) const; std::wstring Text(const std::wstring & name, const wchar_t * def) const; std::wstring Text(const std::wstring & name, const std::wstring & def) const; // returns a reference // if there is no such an option then a new one (def value) is inserted std::wstring & TextRef(const wchar_t * name); std::wstring & TextRef(const wchar_t * name, const wchar_t * def); std::wstring & TextRef(const std::wstring & name, const wchar_t * def); std::wstring & TextRef(const std::wstring & name, const std::wstring & def); // returns UTF-8 string std::string TextA(const wchar_t * name) const; std::string TextA(const wchar_t * name, const char * def) const; std::string TextA(const std::wstring & name, const char * def) const; std::string TextA(const std::wstring & name, const std::string & def) const; std::string TextA(const wchar_t * name, const wchar_t * def) const; std::string TextA(const std::wstring & name, const wchar_t * def) const; std::string TextA(const std::wstring & name, const std::wstring & def) const; int Int(const wchar_t * name, int def = 0); int Int(const std::wstring & name, int def = 0); unsigned int UInt(const wchar_t * name, unsigned int def = 0); unsigned int UInt(const std::wstring & name, unsigned int def = 0); long Long(const wchar_t * name, long def = 0); long Long(const std::wstring & name, long def = 0); unsigned long ULong(const wchar_t * name, unsigned long def = 0); unsigned long ULong(const std::wstring & name, unsigned long def = 0); long long LongLong(const wchar_t * name, long long def = 0); long long LongLong(const std::wstring & name, long long def = 0); unsigned long long ULongLong(const wchar_t * name, unsigned long long def = 0); unsigned long long ULongLong(const std::wstring & name, unsigned long long def = 0); size_t Size(const wchar_t * name, size_t def = 0); size_t Size(const std::wstring & name, size_t def = 0); bool Bool(const wchar_t * name, bool def = false); bool Bool(const std::wstring & name, bool def = false); /* * * methods for adding a new value * * */ std::wstring & FindAdd(const wchar_t * name); std::wstring & FindAdd(const std::wstring & name); std::wstring & FindAdd(const WTextStream & name); std::wstring & Add(const wchar_t * name, bool value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, bool value, bool replace_existing = true); std::wstring & Add(const wchar_t * name, int value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, int value, bool replace_existing = true); std::wstring & Add(const wchar_t * name, long value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, long value, bool replace_existing = true); std::wstring & Add(const wchar_t * name, size_t value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, size_t value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, const std::wstring & value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, const wchar_t * value, bool replace_existing = true); std::wstring & Add(const wchar_t * name, const wchar_t * value, bool replace_existing = true); std::wstring & Add(const wchar_t * name, const std::wstring & value, bool replace_existing = true); std::wstring & Add(const wchar_t * name, const WTextStream & value, bool replace_existing = true); std::wstring & Add(const std::wstring & name, const WTextStream & value, bool replace_existing = true); std::wstring & Add(const WTextStream & name, const WTextStream & value, bool replace_existing = true); void Remove(const wchar_t * name); void Remove(const std::wstring & name); Space & AddSpace(const wchar_t * name); Space & AddSpace(const std::wstring & name); // looking for the first space with the specified name // if there is not such a space those methods return a null pointer Space * FindSpace(const wchar_t * name); Space * FindSpace(const std::wstring & name); // looking for the first space with the specified name // if there is not such a space then this methods adds such a space Space & FindAddSpace(const wchar_t * name); Space & FindAddSpace(const std::wstring & name); void RemoveSpace(const wchar_t * name); void RemoveSpace(const std::wstring & name); void RemoveSpace(size_t child_index); /* * * raw access to the parsed values * * */ std::wstring name; // space name Table table; // std::map > // childs typedef std::vector Spaces; Spaces spaces; // a parent space // null means a root space Space * parent; /* those methods are used to extract lists return true if such an option exists (but value can be an empty list) */ bool ListText(const wchar_t * name, std::vector & list); bool ListText(const std::wstring & name, std::vector & list); /* serialize the content */ 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); private: mutable std::wstring tmp_name; std::wstring tmp_value; std::wstring tmp_value_text; std::string tmp_value_text_ascii; int CheckIntegerBase(const std::wstring & value, const wchar_t ** save_ptr); unsigned int ToUInt(const std::wstring & value); int ToInt(const std::wstring & value); unsigned long ToULong(const std::wstring & value); long ToLong(const std::wstring & value); unsigned long long ToULongLong(const std::wstring & value); long long ToLongLong(const std::wstring & value); size_t ToSize(const std::wstring & value); bool ToBool(const std::wstring & value); wchar_t ToSmall(wchar_t c); bool EqualNoCase(const wchar_t * str1, const wchar_t * str2); static bool IsWhite(int c); static bool HasWhite(const std::wstring & str); }; template void Space::PrintLevel(Stream & out, bool use_indents, int level) { if( use_indents ) { for(int i=0 ; i void Space::PrintValue(Stream & out, const StringType & str, bool use_quote) { if( use_quote ) out << '\"'; for(size_t i=0 ; i void Space::PrintKey(Stream & out, const std::wstring & str) { bool use_quote = false; if( str.empty() || HasWhite(str) ) use_quote = true; PrintValue(out, str, use_quote); } template void Space::SerializeTableMulti(Stream & out, bool use_indents, int level) const { Table::const_iterator i2; size_t v; for(i2 = table.begin() ; i2 != table.end() ; ++i2) { PrintLevel(out, use_indents, level); PrintKey(out, i2->first); out << L" = "; if( i2->second.size() != 1 ) out << '('; for(v = 0 ; v < i2->second.size() ; ++v) { if( v > 0 ) PrintLevel(out, use_indents, level + i2->first.size() + 3); PrintValue(out, i2->second[v]); if( v + 1 < i2->second.size() ) out << '\n'; } if( i2->second.size() != 1 ) out << ')'; out << '\n'; } } template void Space::Serialize(Stream & out, bool use_indents, bool use_comments, int level) const { if( level > 0 ) { out << '\n'; PrintLevel(out, use_indents, level); if( !name.empty() ) { PrintKey(out, name); out << ' '; } out << L"(\n"; if( use_comments ) { PrintLevel(out, use_indents, level); out << L"# space level " << level << '\n'; } } SerializeTableMulti(out, use_indents, level); for(size_t i=0 ; iSerialize(out, use_indents, use_comments, level+1); if( level > 0 ) { PrintLevel(out, use_indents, level); out << ')'; if( use_comments ) { if( name.empty() ) out << L" # end of unnamed space"; else out << L" # end of space: " << name; out << L" (level " << level << L")"; } out << '\n'; } } } // namespace #endif