// Classic Shell (c) 2009-2016, Ivo Beltchev // Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author #include #include "SettingsParser.h" #include "ResourceHelper.h" #include "StringUtils.h" #include const int MAX_TREE_LEVEL=10; // Reads a file into m_Text bool CSettingsParser::LoadText( const wchar_t *fname ) { // read settings file into buf FILE *f=NULL; if (_wfopen_s(&f,fname,L"rb")) return false; if (!f) return false; fseek(f,0,SEEK_END); int size=ftell(f); fseek(f,0,SEEK_SET); std::vector buf(size); if (size<4 || fread(&buf[0],1,size,f)!=size) { fclose(f); return false; } fclose(f); LoadText(&buf[0],size); return true; } // Reads a text resource into m_Text bool CSettingsParser::LoadText( HMODULE hMod, HRSRC hResInfo ) { HGLOBAL hRes=LoadResource(hMod,hResInfo); int size=SizeofResource(hMod,hResInfo); unsigned char *buf=(unsigned char*)LockResource(hRes); if (!buf) return false; LoadText(buf,size); return true; } void CSettingsParser::LoadText( const unsigned char *buf, int size ) { // copy buf to text and convert to UTF16 if (buf[0]==0xFF && buf[1]==0xFE) { // UTF16 int len=(size-2)/2; m_Text.resize(len+1); memcpy(&m_Text[0],&buf[2],size-2); m_Text[len]=0; } else if (buf[0]==0xEF && buf[1]==0xBB && buf[2]==0xBF) { // UTF8 int len=MultiByteToWideChar(CP_UTF8,0,(const char*)&buf[3],size-3,NULL,0); m_Text.resize(len+1); MultiByteToWideChar(CP_UTF8,0,(const char*)&buf[3],size-3,&m_Text[0],len); m_Text[len]=0; } else { // ACP int len=MultiByteToWideChar(CP_ACP,0,(const char*)&buf[0],size,NULL,0); m_Text.resize(len+1); MultiByteToWideChar(CP_UTF8,0,(const char*)&buf[0],size,&m_Text[0],len); m_Text[len]=0; } } void CSettingsParser::LoadText( const wchar_t *buf, int size ) { m_Text.resize(size+1); memcpy(&m_Text[0],buf,size*2); m_Text[size]=0; } // Splits m_Text into m_Lines void CSettingsParser::ParseText( void ) { if (m_Text.empty()) return; // split into lines wchar_t *str=&m_Text[0]; while (*str) { if (*str!=';') // ignore lines starting with ; { // trim leading whitespace while (*str==' ' || *str=='\t') str++; m_Lines.push_back(str); } wchar_t *p1=wcschr(str,'\r'); wchar_t *p2=wcschr(str,'\n'); wchar_t *end=&m_Text[m_Text.size()-1]; if (p1) end=p1; if (p2 && p2str && (*end==' ' || *end=='\t')) end--; *end=0; str=next; } } // Filters the settings that belong to the given language // languages is a 00-terminated list of language names ordered by priority void CSettingsParser::FilterLanguages( const wchar_t *languages ) { std::vector lines; lines.swap(m_Lines); for (const wchar_t *lang=languages;*lang;lang+=wcslen(lang)+1) { size_t langLen=wcslen(lang); for (size_t i=0;i::const_reverse_iterator it=m_Lines.rbegin();it!=m_Lines.rend();++it) { const wchar_t *str=*it; if (_wcsnicmp(name,str,len)==0) { str+=len; while (*str==' ' || *str=='\t') str++; if (*str!='=') continue; str++; while (*str==' ' || *str=='\t') str++; return str; } } return NULL; } // Frees all resources void CSettingsParser::Reset( void ) { m_Lines.clear(); m_Text.clear(); } // Parses a tree structure of items. The rootName setting must be a list of item names. void CSettingsParser::ParseTree( const wchar_t *rootName, std::vector &items ) { const wchar_t *str=FindSetting(rootName); if (str) { CString names[MAX_TREE_LEVEL]; ParseTreeRec(str,items,names,0); } else { TreeItem last={L"",-1}; items.push_back(last); } } int CSettingsParser::ParseTreeRec( const wchar_t *str, std::vector &items, CString *names, int level ) { size_t start=items.size(); while (*str) { wchar_t token[256]; str=GetToken(str,token,_countof(token),L", \t"); if (token[0]) { // bool bFound=false; for (int i=0;i crash! int idx=ParseTreeRec(str2,items,names,level+1); items[i].children=idx; } } } return (int)start; } /////////////////////////////////////////////////////////////////////////////// bool CSkinParser::LoadVariation( const wchar_t *fname ) { m_VarText.swap(m_Text); bool res=LoadText(fname); if (res) { std::vector lines; lines.swap(m_Lines); lines.push_back(L"[TRUE]"); ParseText(); m_Lines.insert(m_Lines.begin(),lines.begin(),lines.end()); } m_VarText.swap(m_Text); return res; } bool CSkinParser::LoadVariation( HMODULE hMod, HRSRC hResInfo ) { m_VarText.swap(m_Text); bool res=LoadText(hMod,hResInfo); if (res) { std::vector lines; lines.swap(m_Lines); lines.push_back(L"[TRUE]"); ParseText(); m_Lines.insert(m_Lines.begin(),lines.begin(),lines.end()); } m_VarText.swap(m_Text); return res; } void CSkinParser::Reset( void ) { CSettingsParser::Reset(); m_VarText.clear(); } static const wchar_t *g_OptionNames[SKIN_OPTION_TYPE_COUNT]={ L"OPTION ", L"OPTION_NUMBER ", L"OPTION_STRING ", L"OPTION_COLOR ", L"OPTION_IMAGE ", }; // Parses the option from m_Lines[index]. Returns false if index is out of bounds bool CSkinParser::ParseOption( CString &name, TSkinOptionType &type, CString &label, bool &value, CString &condition, CString &disValue, int index ) { if (index<0 || index>=(int)m_Lines.size()) return false; name.Empty(); wchar_t buf[256]; const wchar_t *line=m_Lines[index]; if (_wcsnicmp(line,L"OPTION",6)!=0) return true; type=SKIN_OPTION_NONE; for (int i=0;i lines; lines.swap(m_Lines); bool bEnable=true; for (size_t i=0;i_countof(condition)-1) continue; // too long memcpy(condition,line+1,len*2); condition[len]=0; // evaluate condition if (EvalCondition(condition,values,count)==1) bEnable=true; continue; } if (bEnable) m_Lines.push_back(line); } } // Substitutes the provided macro strings void CSkinParser::ApplyMacros( const std::vector> ¯os ) { std::vector names; for (std::vector>::const_iterator it=macros.begin();it!=macros.end();++it) { wchar_t name[256]; Sprintf(name,_countof(name),L"@%s@",it->first); names.push_back(name); } for (std::vector::iterator it=m_Lines.begin();it!=m_Lines.end();++it) { if (wcschr(*it,'@')) { CString string=*it; for (size_t i=0;i=_countof(opStack)) return -1; // too much nesting opStack[osp]=TYPE_PAR; osp++; condition++; continue; } if (*condition==')') { bool found=false; while (osp>0) { osp--; if (opStack[osp]==TYPE_PAR) { found=true; break; } if (!ApplyOperator(valStack,vsp,opStack[osp])) return -1; // invalid operation } if (!found) return -1; // too many ) condition++; continue; } // find token const wchar_t *end=condition; while (*end && *end!=' ' && *end!='\t' && *end!='(' && *end!=')') end++; int len=(int)(end-condition); if (len>=sizeof(token)) return -1; // too long token memcpy(token,condition,len*2); token[len]=0; condition=end; while (*condition==' ' || *condition=='\t') condition++; if (_wcsicmp(token,L"and")==0 || _wcsicmp(token,L"or")==0) { while (osp>0 && opStack[osp-1]!=TYPE_PAR) { osp--; if (!ApplyOperator(valStack,vsp,opStack[osp])) return -1; // invalid operation } if (osp>=_countof(opStack)) return -1; // too much nesting opStack[osp]=(token[0]=='a' || token[0]=='A')?TYPE_AND:TYPE_OR; osp++; } else if (_wcsicmp(token,L"not")==0) { while (osp>0 && opStack[osp-1]==TYPE_NOT) { osp--; if (!ApplyOperator(valStack,vsp,opStack[osp])) return -1; // invalid operation } if (osp>=_countof(opStack)) return -1; // too much nesting opStack[osp]=TYPE_NOT; osp++; } else { if (vsp>=_countof(valStack)) return -1; // too much nesting bool bValue=false; if (_wcsicmp(token,L"true")==0) bValue=true; else { for (int i=0;i0) { osp--; if (opStack[osp]==TYPE_PAR) return -1; // unclosed ( if (!ApplyOperator(valStack,vsp,opStack[osp])) return -1; // invalid operation } if (vsp!=1) return -1; // unbalanced expression return valStack[0]?1:0; }