568 lines
16 KiB
C++
568 lines
16 KiB
C++
/*
|
|
* This file is a part of PikoTools
|
|
* and is distributed under the (new) BSD licence.
|
|
* Author: Tomasz Sowa <t.sowa@ttmath.org>
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2010-2018, 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 <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#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<std::wstring> Value;
|
|
typedef std::map<std::wstring, Value> 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
|
|
*
|
|
*
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* their working in O(log)
|
|
* can return a null pointer
|
|
*
|
|
*/
|
|
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;
|
|
|
|
|
|
|
|
// O(n) complexity
|
|
Value * GetValueNoCase(const wchar_t * name);
|
|
Value * GetValueNoCase(const std::wstring & name);
|
|
const Value * GetValueNoCase(const wchar_t * name) const;
|
|
const Value * GetValueNoCase(const std::wstring & name) const;
|
|
|
|
|
|
// 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 = 0) const;
|
|
std::wstring Text(const std::wstring & name, const wchar_t * def = 0) 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 = 0);
|
|
std::wstring & TextRef(const std::wstring & name, const wchar_t * def = 0);
|
|
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) const;
|
|
int Int(const std::wstring & name, int def = 0) const;
|
|
unsigned int UInt(const wchar_t * name, unsigned int def = 0) const;
|
|
unsigned int UInt(const std::wstring & name, unsigned int def = 0) const;
|
|
|
|
long Long(const wchar_t * name, long def = 0) const;
|
|
long Long(const std::wstring & name, long def = 0) const;
|
|
unsigned long ULong(const wchar_t * name, unsigned long def = 0) const;
|
|
unsigned long ULong(const std::wstring & name, unsigned long def = 0) const;
|
|
|
|
long long LongLong(const wchar_t * name, long long def = 0) const;
|
|
long long LongLong(const std::wstring & name, long long def = 0) const;
|
|
unsigned long long ULongLong(const wchar_t * name, unsigned long long def = 0) const;
|
|
unsigned long long ULongLong(const std::wstring & name, unsigned long long def = 0) const;
|
|
|
|
size_t Size(const wchar_t * name, size_t def = 0) const;
|
|
size_t Size(const std::wstring & name, size_t def = 0) const;
|
|
|
|
bool Bool(const wchar_t * name, bool def = false) const;
|
|
bool Bool(const std::wstring & name, bool def = false) const;
|
|
|
|
|
|
|
|
/*
|
|
*
|
|
* 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<std::wstring, std::vector<std::wstring> >
|
|
|
|
// childs
|
|
typedef std::vector<Space*> 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<std::wstring> & list);
|
|
bool ListText(const std::wstring & name, std::vector<std::wstring> & list);
|
|
|
|
|
|
/*
|
|
serialize the content
|
|
*/
|
|
template<class Stream>
|
|
void Serialize(Stream & out, bool use_indents = false, bool use_comments = false, int level = 0) const;
|
|
|
|
template<class Stream>
|
|
void SerializeTableMulti(Stream & out, bool use_indents, int level) const;
|
|
|
|
template<class Stream, class StringType>
|
|
static void PrintValue(Stream & out, const StringType & str, bool use_quote = true);
|
|
|
|
template<class Stream>
|
|
static void PrintKey(Stream & out, const std::wstring & str);
|
|
|
|
template<class Stream>
|
|
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;
|
|
|
|
static unsigned int ToUInt(const std::wstring & value);
|
|
static int ToInt(const std::wstring & value);
|
|
static unsigned long ToULong(const std::wstring & value);
|
|
static long ToLong(const std::wstring & value);
|
|
static unsigned long long ToULongLong(const std::wstring & value);
|
|
static long long ToLongLong(const std::wstring & value);
|
|
static size_t ToSize(const std::wstring & value);
|
|
static bool ToBool(const std::wstring & value);
|
|
|
|
static bool IsWhite(int c);
|
|
static bool HasWhite(const std::wstring & str);
|
|
|
|
};
|
|
|
|
|
|
|
|
template<class Stream>
|
|
void Space::PrintLevel(Stream & out, bool use_indents, int level)
|
|
{
|
|
if( use_indents )
|
|
{
|
|
for(int i=0 ; i<level ; ++i)
|
|
out << ' ';
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Stream, class StringType>
|
|
void Space::PrintValue(Stream & out, const StringType & str, bool use_quote)
|
|
{
|
|
if( use_quote )
|
|
out << '\"';
|
|
|
|
for(size_t i=0 ; i<str.size() ; ++i)
|
|
{
|
|
switch(str[i])
|
|
{
|
|
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 << str[i];
|
|
}
|
|
}
|
|
|
|
if( use_quote )
|
|
out << '\"';
|
|
}
|
|
|
|
|
|
template<class Stream>
|
|
void Space::PrintKey(Stream & out, const std::wstring & str)
|
|
{
|
|
bool use_quote = false;
|
|
|
|
// CHECK ME
|
|
// HasWhite doesn't take a new line into account, is it correct to use it here?
|
|
if( str.empty() || HasWhite(str) )
|
|
use_quote = true;
|
|
|
|
PrintValue(out, str, use_quote);
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Stream>
|
|
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<class Stream>
|
|
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 ; i<spaces.size() ; ++i)
|
|
spaces[i]->Serialize(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
|