diff --git a/confparser/confparser.cpp b/confparser/confparser.cpp index a35d0ca..37bd555 100755 --- a/confparser/confparser.cpp +++ b/confparser/confparser.cpp @@ -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() { diff --git a/confparser/confparser.h b/confparser/confparser.h index 9e5b02b..8bfaf6b 100755 --- a/confparser/confparser.h +++ b/confparser/confparser.h @@ -12,59 +12,296 @@ #include #include +#include #include + + +/* +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 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 Value; + typedef std::map 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 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); }; diff --git a/core/config.cpp b/core/config.cpp index 9069e5f..20f4afa 100755 --- a/core/config.cpp +++ b/core/config.cpp @@ -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 & list, const char * name) +{ + ListText(list, std::string(name)); +} + + +void Config::ListText(std::vector & 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) diff --git a/core/config.h b/core/config.h index 5c9085b..5b02dd7 100755 --- a/core/config.h +++ b/core/config.h @@ -44,6 +44,9 @@ private: bool Bool(const char * name, bool def); bool Bool(const std::string & name, bool def); + void ListText(std::vector & list, const char * name); + void ListText(std::vector & list, const std::string & name); + std::string default_str; int default_int; bool default_bool; diff --git a/core/htmlfilter.cpp b/core/htmlfilter.cpp index 7adb771..c88dbe2 100755 --- a/core/htmlfilter.cpp +++ b/core/htmlfilter.cpp @@ -1024,7 +1024,8 @@ void HTMLFilter::CheckExceptions() IsLastTag("br") || IsLastTag("hr") || IsLastTag("img") || - IsLastTag("link") ) + IsLastTag("link") || + IsLastTag("area") ) { LastItem().type = Item::simple; PopStack(); diff --git a/core/locale.cpp b/core/locale.cpp index da1eaf8..0984401 100755 --- a/core/locale.cpp +++ b/core/locale.cpp @@ -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; diff --git a/core/locale.h b/core/locale.h index 69c53b0..7010027 100755 --- a/core/locale.h +++ b/core/locale.h @@ -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 loc_tab; + std::vector loc_tab; ConfParser loc_parser; std::string file_name; std::string empty;