fixed: in htmlfilter: <area> should be treated as single tag

changed: ConfParser is abble to recognize lists


git-svn-id: svn://ttmath.org/publicrep/winix/trunk@623 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
Tomasz Sowa 2010-07-21 17:24:16 +00:00
parent 93da32cfb3
commit e4683b9a05
7 changed files with 521 additions and 116 deletions

View File

@ -10,10 +10,32 @@
#include "confparser.h"
ConfParser::ConfParser()
{
separator = '=';
commentary = '#';
// you can change this separators to what you want
// you shoud not use only white characters here (as expected by IsWhite() method)
// and new line characters ('\n')
separator = '=';
commentary = '#';
list_start = '(';
list_end = ')';
list_delimiter = ',';
split_single = false;
skip_empty = false;
}
void ConfParser::SplitSingle(bool split)
{
split_single = split;
}
void ConfParser::SkipEmpty(bool skip)
{
skip_empty = skip;
}
@ -21,81 +43,122 @@ ConfParser::Status ConfParser::Parse(const char * file_name)
{
line = 1;
table.clear();
table_single.clear();
file.clear();
file.open( file_name );
if( !file )
return cant_open_file;
ReadChar();
status = ParseFile();
file.close();
if( file )
{
status = ParseFile();
file.close();
}
else
{
status = cant_open_file;
}
return status;
}
ConfParser::Status ConfParser::Parse(const std::string & file_name)
{
return Parse(file_name.c_str());
}
ConfParser::Status ConfParser::ParseFile()
{
ReadChar();
SkipWhiteLines();
while( lastc != -1 )
{
if( ReadVariable() )
{
if( lastc != separator )
return syntax_error;
ReadChar(); // skipping separator
if( !ReadValue() )
return syntax_error;
if( !ReadVariable() )
return syntax_error;
//table.insert( std::make_pair(variable, value) );
table[variable] = value;
}
if( lastc == commentary )
SkipLine();
if( lastc != -1 && lastc != '\n' )
if( lastc != separator )
return syntax_error;
ReadChar(); // skipping '\n' if was
if( !ReadValue() )
return syntax_error;
AddOption();
SkipWhite();
if( lastc != -1 && lastc != '\n' )
return syntax_error; // some characters have left at the end of an option
SkipWhiteLines();
}
return ok;
}
bool ConfParser::IsVariableChar(int c)
{
if( (c>='a' && c<='z') ||
(c>='A' && c<='Z') ||
(c>='0' && c<='9') ||
c=='.' || c==',' || c=='_' )
return true;
return false;
}
bool ConfParser::IsValueSimpleChar(int c)
void ConfParser::AddOption()
{
//if( c==-1 || c=='\n' || IsWhite(c) || c==commentary )
if( c==-1 || c=='\n' || c==commentary )
return false;
return true;
if( value.empty() && skip_empty )
{
DeleteFromTable(variable);
DeleteFromTableSingle(variable);
return;
}
if( split_single && value.size() == 1 )
{
table_single[variable] = value[0];
DeleteFromTable(variable);
}
else
{
table[variable] = value;
DeleteFromTableSingle(variable);
}
}
void ConfParser::DeleteFromTable(const std::string & var)
{
Table::iterator i = table.find(var);
if( i != table.end() )
table.erase(i);
}
void ConfParser::DeleteFromTableSingle(const std::string & var)
{
TableSingle::iterator i = table_single.find(var);
if( i != table_single.end() )
table_single.erase(i);
}
bool ConfParser::ReadVariable()
{
variable.clear();
SkipWhite();
while( IsVariableChar(lastc) )
@ -114,38 +177,87 @@ return !variable.empty();
bool ConfParser::ReadValue()
{
value.clear();
ReadChar(); // skipping separator '='
SkipWhite();
if( lastc == '"' )
// quoted value
return ReadValueQuoted();
if( lastc == list_start )
return ReadValueList();
else
return ReadValueSimple();
return ReadValueNoList();
}
bool ConfParser::ReadValueList()
{
ReadChar(); // skipping first list character '('
SkipWhiteLines(); // lists can be split into several lines
while( lastc != -1 && lastc != list_end )
{
if( !ReadValueNoList(true) )
return false;
if( lastc == list_delimiter )
ReadChar();
SkipWhiteLines();
}
if( lastc != list_end )
return false;
ReadChar(); // skipping last list character ')'
SkipWhite();
return true;
}
bool ConfParser::ReadValueNoList(bool use_list_delimiter)
{
bool res;
value_item.clear();
if( lastc == '"' )
{
res = ReadValueQuoted(); // quoted value
if( res )
value.push_back(value_item);
}
else
{
res = ReadValueSimple(use_list_delimiter);
if( res && !value_item.empty() )
value.push_back(value_item);
}
return res;
}
bool ConfParser::ReadValueQuoted()
{
ReadChar(); // skipping the first quote
ReadChar(); // skipping the first quote
while( lastc != '\n' && lastc != '"' && lastc != -1 )
while( lastc != '"' && lastc != -1 )
{
if( lastc == '\\' )
ReadChar();
value += lastc;
value_item += lastc;
ReadChar();
}
if( lastc != '"' )
return false;
ReadChar(); // skipping the last quote
ReadChar(); // skipping the last quote
SkipWhite();
return true;
@ -153,17 +265,25 @@ return true;
bool ConfParser::ReadValueSimple()
bool ConfParser::ReadValueSimple(bool use_list_delimiter)
{
while( IsValueSimpleChar(lastc) )
int list_delimiter1 = -1;
int list_delimiter2 = -1;
if( use_list_delimiter )
{
value += lastc;
list_delimiter1 = list_delimiter;
list_delimiter2 = list_end;
}
while( lastc!=-1 && lastc!='\n' && lastc!=commentary &&
lastc!=list_delimiter1 && lastc!=list_delimiter2 )
{
value_item += lastc;
ReadChar();
}
Trim(value);
Trim(value_item);
SkipWhite();
return true;
@ -183,6 +303,9 @@ return lastc;
bool ConfParser::IsWhite(int c)
{
// dont use '\n' here
// 13 (\r) is at the end of a line in a dos file \r\n
// 160 is an unbreakable space
if( c==' ' || c=='\t' || c==13 || c==160 )
return true;
@ -193,11 +316,27 @@ return false;
void ConfParser::SkipWhite()
{
while( IsWhite(lastc) )
ReadChar();
while( IsWhite(lastc) || lastc == commentary )
{
if( lastc == commentary )
SkipLine();
else
ReadChar();
}
}
void ConfParser::SkipWhiteLines()
{
while( IsWhite(lastc) || lastc == commentary || lastc=='\n' )
{
if( lastc == commentary )
SkipLine();
else
ReadChar();
}
}
void ConfParser::SkipLine()
{

View File

@ -12,59 +12,296 @@
#include <fstream>
#include <string>
#include <vector>
#include <map>
/*
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:
ConfParser parser;
parser.Parse("/path/to/config");
if( parser.status == ConfParser::ok )
{
// the whole config we have in parser.table (parser.table_single)
}
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
*/
class ConfParser
{
public:
ConfParser();
/*
status of parsing
*/
enum Status { ok, cant_open_file, syntax_error };
ConfParser();
Status Parse(const char * file_name);
// last status
/*
the last status of parsing, set by Parse() methods
*/
Status status;
/*
the main methods used to parse
file_name is the path to a file
*/
Status Parse(const char * file_name);
Status Parse(const std::string & file_name);
// line in which there is a syntax_error
/*
a number of a line in which there is a syntax_error
*/
int line;
typedef std::map<std::string, std::string> Table;
/*
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::string> Value;
typedef std::map<std::string, Value> Table;
Table table;
/*
if your config file consists mainly of single forms such as:
option = value
option2 = value2
then you can call SplitSingle(true) for not inserting single values to
previous 'table' but instead to 'table_single'
table_single as the second parameter takes only std::string (instead of the whole std::vector)
so you can save a little memory from not using std::vector
*/
typedef std::map<std::string, std::string> TableSingle;
TableSingle table_single;
/*
if your list consists of only one item, e.g:
option1 = value 1
option2 = "value 2"
option3 = ( "value 3" )
then if you call SplitSingle(true) then such values will be stored in
'table_single' instead of 'table' map
default: false
*/
void SplitSingle(bool split);
/*
if true then empty lists, e.g:
option =
option2 = ()
will be omitted (not inserted to 'table' or 'table_single')
default: false
*/
void SkipEmpty(bool skip);
private:
// last read variable, value
std::string variable, value;
/*
last read variable (option)
*/
std::string variable;
// separator between a variable and a value, usually '='
/*
last read list item
*/
std::string value_item;
/*
last read list
*/
Value value;
/*
separator between a variable and a value, default: '='
*/
int separator;
// commentary char
/*
commentary char, default: '#'
*/
int commentary;
// last read char
/*
list starting character, default: '('
*/
int list_start;
/*
list ending character, default: ')'
*/
int list_end;
/*
list delimiter, default: ','
*/
int list_delimiter;
/*
last read char
*/
int lastc;
// current file
/*
current file
*/
std::ifstream file;
Status ParseFile();
/*
if true then lists with one item will be put into 'table_single' table
default: false
*/
bool split_single;
/*
if true then empty lists, e.g:
option =
option2 = ()
will be omitted (not inserted to 'table' or 'table_single')
default: false
*/
bool skip_empty;
Status ParseFile();
void AddOption();
void DeleteFromTable(const std::string & var);
void DeleteFromTableSingle(const std::string & var);
bool IsVariableChar(int c);
bool IsValueSimpleChar(int c);
bool ReadVariable();
bool ReadValue();
bool ReadValueList();
bool ReadValueNoList(bool use_list_delimiter = false);
bool ReadValueQuoted();
bool ReadValueSimple();
bool ReadValueSimple(bool use_list_delimiter = false);
int ReadChar();
bool IsWhite(int c);
bool IsVariableChar(int c);
void SkipWhite();
void SkipWhiteLines();
void SkipLine();
void Trim(std::string & s);
};

View File

@ -72,7 +72,8 @@ bool Config::ReadConfig(bool errors_to_stdout_)
log << log2 << "Config: reading a config file" << logend;
ConfParser::Status status = conf_parser.Parse( data.config_file.c_str() );
conf_parser.SplitSingle(true);
ConfParser::Status status = conf_parser.Parse( data.config_file );
if( status == ConfParser::ok )
@ -141,10 +142,6 @@ void Config::AssignValues()
data.session_file = Text("session_file");
data.compression = Bool("compression", true);
std::string p = Text("plugin");
data.plugin_file.push_back(p);
data.html_filter = Bool("html_filter", true);
data.locale_str = Text("locale", "en");
@ -153,6 +150,8 @@ void Config::AssignValues()
data.title_separator = Text("title_separator", " / ");
ListText(data.plugin_file, "plugin");
}
@ -173,9 +172,9 @@ std::string Config::Text(const char * name, const char * def)
std::string Config::Text(const std::string & name, const std::string & def)
{
ConfParser::Table::iterator i = conf_parser.table.find(name);
ConfParser::TableSingle::iterator i = conf_parser.table_single.find(name);
if( i == conf_parser.table.end() )
if( i == conf_parser.table_single.end() )
return def;
return i->second;
@ -197,9 +196,9 @@ int Config::Int(const char * name, int def)
int Config::Int(const std::string & name, int def)
{
ConfParser::Table::iterator i = conf_parser.table.find(name);
ConfParser::TableSingle::iterator i = conf_parser.table_single.find(name);
if( i == conf_parser.table.end() || i->second.empty() )
if( i == conf_parser.table_single.end() || i->second.empty() )
return def;
long res = (i->second[0] == '0')? strtol(i->second.c_str() + 1, 0, 8) : strtol(i->second.c_str(), 0, 10);
@ -222,9 +221,9 @@ bool Config::Bool(const char * name, bool def)
bool Config::Bool(const std::string & name, bool def)
{
ConfParser::Table::iterator i = conf_parser.table.find(name);
ConfParser::TableSingle::iterator i = conf_parser.table_single.find(name);
if( i == conf_parser.table.end() || i->second.empty() )
if( i == conf_parser.table_single.end() || i->second.empty() )
return def;
bool res = false;
@ -239,6 +238,34 @@ return res;
}
// in lists we don't use default values
void Config::ListText(std::vector<std::string> & list, const char * name)
{
ListText(list, std::string(name));
}
void Config::ListText(std::vector<std::string> & list, const std::string & name)
{
list.clear();
ConfParser::TableSingle::iterator i = conf_parser.table_single.find(name);
if( i != conf_parser.table_single.end() )
{
list.push_back(i->second);
return;
}
ConfParser::Table::iterator z = conf_parser.table.find(name);
if( z != conf_parser.table.end() )
{
list = z->second;
return;
}
}
void Config::NoLastSlash(std::string & s)

View File

@ -44,6 +44,9 @@ private:
bool Bool(const char * name, bool def);
bool Bool(const std::string & name, bool def);
void ListText(std::vector<std::string> & list, const char * name);
void ListText(std::vector<std::string> & list, const std::string & name);
std::string default_str;
int default_int;
bool default_bool;

View File

@ -1024,7 +1024,8 @@ void HTMLFilter::CheckExceptions()
IsLastTag("br") ||
IsLastTag("hr") ||
IsLastTag("img") ||
IsLastTag("link") )
IsLastTag("link") ||
IsLastTag("area") )
{
LastItem().type = Item::simple;
PopStack();

View File

@ -22,9 +22,9 @@ Locale::Locale()
void Locale::AddLocale(Lang lang)
{
ConfParser::Table::iterator i = loc_parser.table.begin();
ConfParser::TableSingle::iterator i = loc_parser.table_single.begin();
for( ; i != loc_parser.table.end() ; ++i)
for( ; i != loc_parser.table_single.end() ; ++i)
loc_tab[lang][i->first] = i->second;
}
@ -41,40 +41,37 @@ void Locale::ReadFile(const char * dir, const char * dir_def, Lang lang, const c
loc_tab[lang].clear();
bool read = false;
if( dir_def )
{
file_name = dir_def;
file_name += '/';
file_name += file;
if( loc_parser.Parse(file_name.c_str()) == ConfParser::ok )
{
read = true;
AddLocale(lang);
log << log3 << "Locale: read locale from: " << file_name << logend;
}
}
if( dir )
{
file_name = dir;
file_name += '/';
file_name += file;
if( loc_parser.Parse(file_name.c_str()) == ConfParser::ok )
{
read = true;
AddLocale(lang);
log << log3 << "Locale: read locale from: " << file_name << logend;
}
}
if( dir_def && ReadFile(dir_def, lang, file) )
read = true;
if( dir && ReadFile(dir, lang, file) )
read = true;
if( !read )
log << log1 << "Locale: cant open file for locale: " << file << logend;
log << log1 << "Locale: can't open locale's file: " << file << logend;
}
bool Locale::ReadFile(const char * dir, Lang lang, const char * file)
{
bool read = false;
file_name = dir;
file_name += '/';
file_name += file;
loc_parser.SplitSingle(true);
if( loc_parser.Parse(file_name) == ConfParser::ok )
{
read = true;
AddLocale(lang);
log << log3 << "Locale: read locale from: " << file_name << logend;
}
return read;
}
void Locale::Read(const char * dir, const char * dir_def)
{
@ -126,7 +123,7 @@ bool Locale::IsKey(const std::string & key, Lang lang) const
}
// looking in the lang language
ConfParser::Table::const_iterator i = loc_tab[lang].find(key);
ConfParser::TableSingle::const_iterator i = loc_tab[lang].find(key);
if( i != loc_tab[lang].end() )
return true;
@ -168,7 +165,7 @@ const std::string & Locale::Get(const std::string & key, Lang lang) const
}
// looking in the lang language
ConfParser::Table::const_iterator i = loc_tab[lang].find(key);
ConfParser::TableSingle::const_iterator i = loc_tab[lang].find(key);
if( i != loc_tab[lang].end() )
return i->second;

View File

@ -56,9 +56,10 @@ private:
void AddLocale(Lang lang);
void ReadFile(const char * dir, const char * dir_def, Lang lang, const char * file);
bool ReadFile(const char * dir, Lang lang, const char * file);
std::vector<ConfParser::Table> loc_tab;
std::vector<ConfParser::TableSingle> loc_tab;
ConfParser loc_parser;
std::string file_name;
std::string empty;