/* * This file is a part of EZC -- Easy templating in C++ library * and is distributed under the BSD 3-Clause licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2007-2014, 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. */ #include "patternparser.h" namespace Ezc { PatternParser::PatternParser() { allow_include = true; pblocks = 0; include_level_max = 100; delete_white_text_items = false; } void PatternParser::Directory(const char * dir, const char * dir2) { directory.clear(); directory2.clear(); if( dir ) PT::UTF8ToWide(dir, directory); if( dir2 ) PT::UTF8ToWide(dir2, directory2); } void PatternParser::Directory(const std::string & dir) { PT::UTF8ToWide(dir, directory); directory2.clear(); } void PatternParser::Directory(const std::string & dir, const std::string & dir2) { PT::UTF8ToWide(dir, directory); PT::UTF8ToWide(dir2, directory2); } void PatternParser::Directory(const wchar_t * dir, const wchar_t * dir2) { directory.clear(); directory2.clear(); if( dir ) directory = dir; if( dir2 ) directory2 = dir2; } void PatternParser::Directory(const std::wstring & dir) { directory = dir; directory2.clear(); } void PatternParser::Directory(const std::wstring & dir, const std::wstring & dir2) { directory = dir; directory2 = dir2; } void PatternParser::SetBlocks(Blocks & blocks) { pblocks = &blocks; } void PatternParser::SetCommentary(const char * com_start, const char * com_stop) { PT::UTF8ToWide(com_start, commentary_start); PT::UTF8ToWide(com_stop, commentary_stop); } void PatternParser::SetCommentary(const std::string & com_start, const std::string & com_stop) { PT::UTF8ToWide(com_start, commentary_start); PT::UTF8ToWide(com_stop, commentary_stop); } void PatternParser::SetCommentary(const wchar_t * com_start, const wchar_t * com_stop) { commentary_start = com_start; commentary_stop = com_stop; } void PatternParser::SetCommentary(const std::wstring & com_start, const std::wstring & com_stop) { commentary_start = com_start; commentary_stop = com_stop; } void PatternParser::CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg) { out = commentary_start; out += L"Ezc: "; out += type; if( arg ) { out += ' '; out += arg; } out += commentary_stop; } void PatternParser::ParseFile(const std::string & file_name, Pattern & pattern) { ParseFile(file_name.c_str(), pattern); } void PatternParser::ParseFile(const char * file_name, Pattern & pattern) { pat = &pattern; PT::UTF8ToWide(file_name, pat->item_root.file_name); include_level = 0; CreateTreeReadIncludeSkipAllowFlag(pat->item_root); } void PatternParser::ParseFile(const std::wstring & file_name, Pattern & pattern) { ParseFile(file_name.c_str(), pattern); } void PatternParser::ParseFile(const wchar_t * file_name, Pattern & pattern) { pat = &pattern; pat->item_root.file_name = file_name; include_level = 0; CreateTreeReadIncludeSkipAllowFlag(pat->item_root); } void PatternParser::ParseString(const char * str, Pattern & pattern) { PT::UTF8ToWide(str, string_content); ParseString(string_content.c_str(), pattern); string_content.clear(); } void PatternParser::ParseString(const std::string & str, Pattern & pattern) { ParseString(str.c_str(), pattern); } void PatternParser::ParseString(const wchar_t * str, Pattern & pattern) { pat = &pattern; itext = str; include_level = 0; CreateTree(pat->item_root); } void PatternParser::ParseString(const std::wstring & str, Pattern & pattern) { ParseString(str.c_str(), pattern); } void PatternParser::AllowInclude(bool allow) { allow_include = allow; } void PatternParser::DeleteWhiteTextItems(bool del) { delete_white_text_items = del; } void PatternParser::SetIncludeMax(int include_max) { include_level_max = include_max; } bool PatternParser::HasFileAtBeginning(const wchar_t * path, const wchar_t * file) { for(; *path && *file; ++path, ++file) { if( *path != *file ) return false; } if( *file != 0 ) return false; // "\" is from a dos path syntax if( *path==0 || *path=='\\' || *path=='/' ) return true; return false; } /* ".." is not allowed in the file path you cannot go up from your template directory */ bool PatternParser::IsFileCorrect(const wchar_t * name) { while( *name ) { if( HasFileAtBeginning(name, L"..") ) return false; // looking for the next slash of backslash while( *name && *name!='\\' && *name!='/' ) name += 1; // skipping the slash (or backslash) if( *name ) name += 1; } return true; } /* 'name' must be a relative path - without a slash or backslash */ void PatternParser::ReadFile(const std::wstring & name, std::wstring & result) { ReadFile(name.c_str(), result); } /* 'name' must be a relative path - without a slash or backslash */ void PatternParser::ReadFile(const wchar_t * name, std::wstring & result) { if( !IsFileCorrect(name) ) { CreateMsg(result, L"incorrect file name: ", name); } else { result.clear(); if( !ReadFileFromDir(directory, name, result) ) if( !ReadFileFromDir(directory2, name, result) ) CreateMsg(result, L"can't open: ", name); } } bool PatternParser::ReadFileFromDir(const std::wstring & dir, const wchar_t * name, std::wstring & result) { if( dir.empty() ) return false; file_name = dir; file_name += '/'; file_name += name; PT::WideToUTF8(file_name, afile_name); std::ifstream file(afile_name.c_str()); if( !file ) { file_name.clear(); afile_name.clear(); return false; } #ifdef EZC_USE_WINIX_LOGGER if( include_level <= 1 ) Winix::log << Winix::log3 << "Ezc: reading pattern: " << afile_name << Winix::logend; else Winix::log << Winix::log3 << " including pattern: " << afile_name << Winix::logend; #endif ReadFile(file, result); file_name.clear(); afile_name.clear(); return true; } void PatternParser::ReadFile(std::ifstream & file, std::wstring & result) { PT::UTF8ToWide(file, result); } int PatternParser::ReadCharInText() { if( *itext==0 || *itext=='[' ) return -1; if( *itext == '\\' ) { if( *(itext+1)=='\\' || *(itext+1)=='[' || *(itext+1)==']' ) ++itext; } return *(itext++); } bool PatternParser::IsWhite(wchar_t c) { // 13 (\r) is from a dos file at the end of a line (\r\n) // 160 is a non-breaking space if( c==' ' || c=='\t' || c==13 || c==160 || c==10 ) return true; return false; } void PatternParser::SkipWhite() { while( IsWhite(*itext) ) ++itext; } void PatternParser::CheckWhiteAndDelete(std::wstring & s) { size_t i; if( s.empty() ) return; for(i=0 ; i='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_' || c=='-' || c=='.' || c=='#'); } bool PatternParser::IsDigit(wchar_t c) { return (c>='0' && c<='9'); } bool PatternParser::IsPositiveNumber(const std::wstring & str) { size_t i; for(i=0 ; i 1 ) item.type = Item::item_err; } // user defined directive void PatternParser::ReadDirectiveNormal(const std::wstring & name, Item & item) { temp_function.Clear(); ReadParams(temp_function); temp_function.name = name; if( IsPositiveNumber(name) ) temp_function.arg = wcstol(name.c_str(), 0, 10); item.functions.clear(); item.functions.push_back(temp_function); item.type = Item::item_normal; } void PatternParser::CreateTreeReadItemDirective(Item & item) { std::wstring name; ++itext; ReadName(name); if ( name == L"if" ) ReadDirectiveIf(item); else if( name == L"if-no" ) ReadDirectiveIfno(item); else if( name == L"if-any" ) ReadDirectiveIfany(item); else if( name == L"if-one" ) ReadDirectiveIfone(item); else if( name == L"if-any-no" ) ReadDirectiveIfanyno(item); else if( name == L"if-one-no" ) ReadDirectiveIfoneno(item); else if( name == L"end" ) ReadDirectiveEnd(item); else if( name == L"else" ) ReadDirectiveElse(item); else if( name == L"for" ) ReadDirectiveFor(item); else if( name == L"include" ) ReadDirectiveInclude(item); else if( name == L"def" ) ReadDirectiveDef(item); else if( name == L"filter" ) ReadDirectiveFilter(item); else if( name == L"ezc" ) ReadDirectiveEzc(item); else if( name == L"block" ) ReadDirectiveBlock(item); else if( name == L"return" ) ReadDirectiveReturn(item); else if( name == L"#" ) ReadDirectiveComment(item); else ReadDirectiveNormal(name, item); CreateTreeReadItemDirectiveCheckEnding(item); } void PatternParser::CreateTreeReadItemText(Item & item) { int c; while( (c = ReadCharInText()) != -1 ) item.text += static_cast(c); if( delete_white_text_items ) CheckWhiteAndDelete(item.text); item.type = Item::item_text; } bool PatternParser::CreateTreeReadItem(Item & item) { item.Clear(); if( *itext == '[' ) { CreateTreeReadItemDirective(item); return true; } else if( *itext ) { CreateTreeReadItemText(item); return true; } // the end of the string return false; } void PatternParser::CreateTreeReadInclude(Item & item) { if( !allow_include ) return; CreateTreeReadIncludeSkipAllowFlag(item); } void PatternParser::CreateTreeReadIncludeSkipAllowFlag(Item & item) { if( item.file_name.empty() ) return; if( include_level > include_level_max ) { #ifdef EZC_USE_WINIX_LOGGER Winix::log << Winix::log1 << "Ezc: \"include\" directive has reached the maximum level" << Winix::logend; #endif return; } ++include_level; std::wstring file_text; // this temporary object must not be global (includes can be nested) ReadFile(item.file_name, file_text); const wchar_t * itext_old = itext; itext = file_text.c_str(); CreateTree(item); itext = itext_old; --include_level; } void PatternParser::CreateTreeReadIf(Item & item) { Item * pitem = item.AddItem(); CreateTree(*pitem); if( pitem->LastItemType() == Item::item_else ) { pitem->DeleteLastItem(); pitem = item.AddItem(); CreateTree(*pitem); } if( pitem->LastItemType() == Item::item_end ) pitem->DeleteLastItem(); } void PatternParser::CreateTreeReadBlock(Item & item) { Item item_block; CreateTree(item_block); if( item_block.LastItemType() == Item::item_end ) item_block.DeleteLastItem(); if( pblocks && item.functions.size()==1 ) pblocks->Insert(item.functions[0].name, item_block); } void PatternParser::CreateTreeReadFor(Item & item) { Item * pitem = item.AddItem(); CreateTree(*pitem); if( pitem->LastItemType() == Item::item_end ) pitem->DeleteLastItem(); } void PatternParser::CreateTree(Item & item) { item.Clear(); item.type = Item::item_container; while( true ) { Item * pitem = item.AddItem(); do { if( !CreateTreeReadItem(*pitem) ) { item.DeleteLastItem(); return; } if( pitem->type == Item::item_block ) CreateTreeReadBlock(*pitem); } while( pitem->type == Item::item_comment || pitem->type == Item::item_block ); if( pitem->type == Item::item_end || pitem->type == Item::item_else ) return; if( pitem->type == Item::item_if || pitem->type == Item::item_ifno || pitem->type == Item::item_ifany || pitem->type == Item::item_ifone || pitem->type == Item::item_ifanyno || pitem->type == Item::item_ifoneno ) CreateTreeReadIf(*pitem); if( pitem->type == Item::item_for || pitem->type == Item::item_filter ) CreateTreeReadFor(*pitem); if( pitem->type == Item::item_include ) CreateTreeReadInclude(*pitem); } } } // namespace Ezc