/* * This file is a part of EZC -- Easy templating in C++ * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2007, 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 "ezc.h" namespace Ezc { std::string CreateMsg(const char * type, const char * arg) { std::ostringstream buffer; buffer << ""; return buffer.str(); } /* * * Pattern * * */ void Pattern::ParseFile(std::string file_name) { file_name.insert(file_name.begin(), '\"'); item_root.directives.clear(); item_root.directives.push_back( file_name ); CreateTreeReadInclude(item_root); } void Pattern::ParseFile(const char * file_name) { ParseFile( std::string(file_name) ); } void Pattern::Directory(const char * d) { directory = d; } void Pattern::Directory(const std::string & d) { directory = d; } bool Pattern::CheckFileName(const char * name) { // very simple testing -- this path 'some..name' is incorrect as well // (in the future it can be changed) for( ; *name ; ++name ) { if( *name=='.' && *(name+1)=='.' ) return false; } return true; } std::string Pattern::ReadFile(const char * name) { // for security reason we can't open a file which has two dots // somewhere in its name '..' if( !CheckFileName(name) ) return CreateMsg("incorrect file name:", name); std::string file_name; if( name[0]!='\\' && name[0]!='/' ) { // name is a relative path file_name += directory; if( !file_name.empty() ) file_name += '/'; } file_name += name; std::ifstream file(file_name.c_str()); if( !file ) return CreateMsg("can't open:", name); // or we can give the whole path here (file_name) else { std::string result; std::getline(file, result, '\0'); return result; } } int Pattern::ReadCharInText() { if( *itext==0 || *itext=='[' ) return -1; if( *itext == '\\' ) { if( *(itext+1)=='\\' || *(itext+1)=='[' || *(itext+1)==']' ) ++itext; } return *(itext++); } void Pattern::SkipWhiteCharacters() { while( *itext==' ' || *itext=='\t' ) ++itext; } std::string Pattern::ReadDirective() { std::string directive; SkipWhiteCharacters(); while( (*itext>='a' && *itext<='z') || (*itext>='A' && *itext<='Z') || (*itext>='0' && *itext<='9') || *itext=='_' || *itext=='-' || *itext=='.' ) { directive += *itext; ++itext; } return directive; } std::string Pattern::ReadString() { std::string directive; SkipWhiteCharacters(); if( *itext != '\"' ) return directive; // string is signed by its first character equal (") directive += '\"'; for( ++itext ; *itext && *itext!='\"' && *itext!='\n' ; ++itext ) directive += *itext; if( *itext != '\"' ) // the second quotation mark (") is missing directive.clear(); else ++itext; // the second quotation mark (") we don't add into the 'directive' return directive; } std::string Pattern::ReadDirectiveOrString() { SkipWhiteCharacters(); if( *itext == '\"' ) { // we've got string return ReadString(); } else { return ReadDirective(); } } void Pattern::CreateTreeReadItemDirectiveCheckEnding(Item & item) { SkipWhiteCharacters(); if( *itext != ']' ) { item.type = Item::item_err; while( *itext!=0 && *itext!=']' ) ++itext; } if( *itext == ']' ) ++itext; } void Pattern::ReadDirectiveIfany(Item & item) { item.type = Item::item_ifany; while( true ) { std::string directive = ReadDirective(); if( directive.empty() ) break; item.directives.push_back(directive); } if( item.directives.empty() ) item.type = Item::item_err; } void Pattern::ReadDirectiveIfone(Item & item) { ReadDirectiveIfany(item); if( item.type == Item::item_ifany ) item.type = Item::item_ifone; } void Pattern::ReadDirectiveIs(Item & item) { item.type = Item::item_is; std::string directive = ReadDirective(); if( !directive.empty() ) { item.directives.push_back(directive); directive = ReadDirectiveOrString(); if( !directive.empty() ) item.directives.push_back(directive); } if( item.directives.size() != 2 ) item.type = Item::item_err; } void Pattern::ReadDirectiveIfindex(Item & item) { item.type = Item::item_ifindex; std::string directive = ReadDirective(); if( !directive.empty() ) { item.directives.push_back(directive); directive = ReadDirective(); if( !directive.empty() ) item.directives.push_back(directive); } if( item.directives.size() != 2 ) item.type = Item::item_err; } void Pattern::ReadDirectiveFor(Item & item) { item.type = Item::item_for; std::string directive = ReadDirective(); if( !directive.empty() ) item.directives.push_back(directive); else item.type = Item::item_err; } void Pattern::ReadDirectiveInclude(Item & item) { item.type = Item::item_include; std::string directive = ReadString(); if( !directive.empty() ) item.directives.push_back(directive); else item.type = Item::item_err; } void Pattern::CreateTreeReadItemDirective(Item & item) { ++itext; std::string directive = ReadDirective(); if( directive == "if-any" ) ReadDirectiveIfany(item); else if( directive == "if-one" ) ReadDirectiveIfone(item); else if( directive == "is" ) ReadDirectiveIs(item); else if( directive == "if-index" ) ReadDirectiveIfindex(item); else if( directive == "end" ) item.type = Item::item_end; else if( directive == "else" ) item.type = Item::item_else; else if( directive == "for" ) ReadDirectiveFor(item); else if( directive == "include" ) ReadDirectiveInclude(item); else { // user defined item.directives.push_back(directive); item.type = Item::item_normal; } CreateTreeReadItemDirectiveCheckEnding(item); } void Pattern::CreateTreeReadItemText(Item & item) { int c; while( (c = ReadCharInText()) != -1 ) item.text += c; item.type = Item::item_text; } bool Pattern::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 Pattern::CreateTreeReadInclude(Item & item) { if( item.directives.empty() || item.directives[0].empty() || item.directives[0][0]!='\"' ) return; std::string file_text = ReadFile( item.directives[0].c_str()+1 ); const char * itext_copy = itext; itext = file_text.c_str(); CreateTree(item); itext = itext_copy; } void Pattern::CreateTreeReadIf(Item & item) { Item * pitem = item.AddItem(); CreateTree(*pitem); if( pitem->LastItemType() == Item::item_else ) { pitem->DeleteLastItem(); pitem = item.AddItem(); CreateTree(*pitem); } pitem->DeleteLastItem(); // it deletes [end] from the tree } void Pattern::CreateTreeReadFor(Item & item) { Item * pitem = item.AddItem(); CreateTree(*pitem); pitem->DeleteLastItem(); // it deletes [end] from the tree } void Pattern::CreateTree(Item & item) { item.Clear(); item.type = Item::item_container; Item item_temp; while( CreateTreeReadItem(item_temp) ) { Item * pitem = item.AddItem(item_temp); if( item_temp.type==Item::item_end || item_temp.type==Item::item_else ) return; if( pitem->type == Item::item_ifany || pitem->type == Item::item_ifone || pitem->type == Item::item_ifindex || pitem->type == Item::item_is ) CreateTreeReadIf(*pitem); if( pitem->type == Item::item_for ) CreateTreeReadFor(*pitem); if( pitem->type == Item::item_include ) CreateTreeReadInclude(*pitem); } } /* * * Pattern::Item * * */ Pattern::Item * Pattern::Item::AddItem(const Pattern::Item * porg) { Item * pitem; if( porg ) pitem = new Item(*porg); else pitem = new Item(); item_table.push_back(pitem); return pitem; } Pattern::Item * Pattern::Item::AddItem(const Pattern::Item & porg) { return AddItem(&porg); } void Pattern::Item::ClearItems() { std::vector::iterator i = item_table.begin(); for( ; i != item_table.end() ; ++i ) delete *i; item_table.clear(); } void Pattern::Item::Clear() { ClearItems(); type = item_none; text.clear(); directives.clear(); } Pattern::Item::ItemType Pattern::Item::LastItemType() { if( item_table.empty() ) return item_none; return item_table.back()->type; } void Pattern::Item::DeleteLastItem() { if( item_table.empty() ) return; delete item_table.back(); item_table.erase( item_table.end() - 1 ); } Pattern::Item::Item() { type = item_none; } Pattern::Item::Item(const Pattern::Item & i) : type(i.type), text(i.text), directives(i.directives) { CopyItemTable(i); } Pattern::Item & Pattern::Item::operator=(const Pattern::Item & i) { type = i.type; text = i.text; directives = i.directives; CopyItemTable(i); return *this; } void Pattern::Item::CopyItemTable(const Pattern::Item & item) { std::vector::const_iterator i = item.item_table.begin(); for( ; i != item.item_table.end() ; ++i) AddItem( *i ); } Pattern::Item::~Item() { ClearItems(); } /* * * Info * * */ Info::Info() { result = false; iter = 0; } /* * * Generator * * */ std::string & Generator::Generate(Pattern & data) { otext.clear(); MakeText( data.item_root ); return otext; } std::string & Generator::String() { return otext; } void Generator::Insert(const std::string & key, UserFunction ufunction) { UserInfo ui; ui.user_function = ufunction; ui.iter = 0; user_info_table.insert( std::make_pair(key, ui) ); } void Generator::MakeTextContainer(Pattern::Item & item) { std::vector::iterator i = item.item_table.begin(); for( ; i != item.item_table.end() ; ++i ) MakeText(**i); } bool Generator::Find(const std::string & key, UserInfo ** user_info) { UserInfoTable::iterator i = user_info_table.find( key ); if( i == user_info_table.end() ) { otext += CreateMsg("can't find", key.c_str() ); return false; } *user_info = &(i->second); return true; } void Generator::MakeTextNormal(Pattern::Item & item) { if( item.directives.size() != 1 ) return; UserInfo * user_info; if( Find(item.directives[0], &user_info) ) { Info info; (user_info->user_function)(info); otext += info.text; } } void Generator::MakeTextIf_go(Pattern::Item & item, bool result) { if( result ) { if( item.item_table.size() > 0 ) MakeText( *item.item_table[0] ); } else { // second element can be (or not -- it's from [else]) if( item.item_table.size() > 1 ) MakeText( *item.item_table[1] ); } } void Generator::MakeTextIfany(Pattern::Item & item) { std::vector::iterator d = item.directives.begin(); unsigned how_many_true = 0; for( ; d != item.directives.end() ; ++d ) { UserInfo * user_info; if( !Find(*d, &user_info) ) return; Info info; (user_info->user_function)(info); if( !info.text.empty() || info.result ) ++how_many_true; } MakeTextIf_go(item, how_many_true == item.directives.size() ); } void Generator::MakeTextIfone(Pattern::Item & item) { std::vector::iterator d = item.directives.begin(); int how_many_true = 0; for( ; d != item.directives.end() ; ++d ) { UserInfo * user_info; if( !Find(*d, &user_info) ) return; Info info; (user_info->user_function)(info); if( !info.text.empty() || info.result ) ++how_many_true; } MakeTextIf_go(item, how_many_true > 0 ); } void Generator::MakeTextIs(Pattern::Item & item) { if( item.directives.size() != 2 ) return; UserInfo * user_info; if( !Find(item.directives[0], &user_info) ) return; bool result = false; Info info1, info2; (user_info->user_function)(info1); if( !item.directives[1].empty() && item.directives[1][0]=='\"' ) { if( std::strcmp(info1.text.c_str(), item.directives[1].c_str()+1) == 0 ) result = true; } else { if( Find(item.directives[1], &user_info) ) { (user_info->user_function)(info2); if( info1.result==info2.result && info1.text==info2.text ) result = true; } } MakeTextIf_go(item, result); } bool Generator::MakeTextIfindexnumber(Pattern::Item & item, UserInfo * user_info, bool & result) { if( item.directives.size() != 2 ) return false; const char * number_text = item.directives[1].c_str(); char * last_char; int number = (int)strtol(number_text, &last_char, 10); if( *last_char == '\0' ) { if( user_info->iter == number ) result = true; } else { otext += CreateMsg("if-index: syntax error"); return false; } return true; } void Generator::MakeTextIfindex(Pattern::Item & item) { if( item.directives.size() != 2 ) return; UserInfo * user_info; bool result = false; if( !Find(item.directives[0], &user_info) ) return; if( item.directives[1] == "odd" ) { if( ( user_info->iter & 1) == 1 ) result = true; } else if( item.directives[1] == "even" ) { if( ( user_info->iter & 1) == 0 ) result = true; } else if( item.directives[1] == "first" ) { if( user_info->iter == 0 ) result = true; } else { if( !MakeTextIfindexnumber(item, user_info, result) ) return; } MakeTextIf_go(item, result); } void Generator::MakeTextFor(Pattern::Item & item) { if( item.directives.size() != 1 ) return; UserInfo * user_info; if( !Find(item.directives[0], &user_info) ) return; Info info; user_info->iter = 0; while( true ) { (user_info->user_function)(info); if( info.text.empty() && !info.result ) break; if( item.item_table.size() > 0 ) MakeText( *item.item_table[0] ); ++info.iter; user_info->iter = info.iter; } } void Generator::MakeText(Pattern::Item & item) { switch( item.type ) { case Pattern::Item::item_text: otext += item.text; break; case Pattern::Item::item_container: MakeTextContainer(item); break; case Pattern::Item::item_normal: MakeTextNormal(item); break; case Pattern::Item::item_ifany: MakeTextIfany(item); break; case Pattern::Item::item_ifone: MakeTextIfone(item); break; case Pattern::Item::item_ifindex: MakeTextIfindex(item); break; case Pattern::Item::item_is: MakeTextIs(item); break; case Pattern::Item::item_for: MakeTextFor(item); break; case Pattern::Item::item_err: otext += CreateMsg("a wrong directive"); break; default: break; } } void Generator::Clear() { user_info_table.clear(); otext.clear(); } } // namespace Ezc