/* Variables Plugin for Miranda-IM (www.miranda-im.org) Copyright 2003-2006 P. Boon This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "variables.h" BOOL (WINAPI *pfnEnableThemeDialogTexture)(HANDLE, DWORD) = 0; static BOOL bWarningShown = FALSE; // unicode on ansi warning /* some handles */ static HANDLE hFormatStringService, hFreeMemoryService, hRegisterVariableService, hGetMMIService, hShowHelpService, hShowHelpExService, hGetIconService; static HANDLE hOptionsHook = NULL, hIconsChangedHook = NULL; HCURSOR hCurSplitNS; struct ParseOptions gParseOpts; extern HINSTANCE hInst; TCHAR *getArguments(TCHAR *string, TCHAR ***aargv, int *aargc) { BOOL bDontParse, bNewArg, bDone; TCHAR *cur, *scur, **argv; int i, argc, brackets; *aargv = NULL; *aargc = 0; argc = brackets = 0; argv = NULL; cur = string; while (*cur == _T(' ')) { cur++; } if (*cur != _T('(')) { return NULL; } cur++; scur = cur-1; bDontParse = bNewArg = bDone = FALSE; while ( (!bDone) && (*cur != _T('\0')) ) { switch (*cur) { case _T(DONTPARSE_CHAR): if (bDontParse) { bDontParse = FALSE; } else { bDontParse = TRUE; } break; case _T(','): if ( (!bDontParse) && (brackets == 0) ) { bNewArg = TRUE; } break; case _T('('): if (!bDontParse) { brackets += 1; } break; case _T(')'): if ( (brackets == 0) && (!bDontParse) ) { bDone = bNewArg = TRUE; } else if ( (brackets > 0) && (!bDontParse) ) { brackets -= 1; } break; } if (bNewArg) { argv = ( TCHAR** )realloc(argv, (argc+1)*sizeof(TCHAR *)); if (argv == NULL) { return NULL; } if (cur > scur) { argv[argc] = ( TCHAR* )malloc((cur-scur+2)*sizeof(TCHAR)); if (argv[argc] == NULL) { return NULL; } memset(argv[argc], '\0', (cur-(scur+1)+1)*sizeof(TCHAR)); _tcsncpy(argv[argc], scur+1, cur-(scur+1)); } else { argv[argc] = _tcsdup(_T("")); } argc += 1; bNewArg = FALSE; scur = cur; } cur++; } // set args if (*(cur-1) == _T(')')) { *aargv = argv; *aargc = argc; } else { for (i=0;ipCount = 0; memcpy(&afi, fi, sizeof(afi)); for (pos = 0;pos < _tcslen(string);pos++) { // string may move in memory, iterate by remembering the position in the string cur = string+pos; // free memory from last iteration, this way we can bail out at any time in the loop if (parsedToken != NULL) free(parsedToken); for (i=0;ieCount += 1; continue; } token = ( TCHAR* )malloc((tcur-scur+1)*sizeof(TCHAR)); if (token == NULL) { fi->eCount += 1; return NULL; } memset(token, '\0', (tcur-scur+1)*sizeof(TCHAR)); _tcsncpy(token, cur+1, tcur-scur); // cur points to FIELD_CHAR or FUNC_CHAR tmpVarPos = -1; tr = NULL; if (*cur==_T(FIELD_CHAR)) { for(i = 0; i < fi->cbTemporaryVarsSize; i += 2) { if (lstrcmp(fi->tszaTemporaryVars[i], token) == 0) { tmpVarPos = i; break; } } } if (tmpVarPos < 0) tr = searchRegister(token, (*cur==_T(FIELD_CHAR))?TRF_FIELD:TRF_FUNCTION); free(token); if (tmpVarPos < 0 && tr == NULL) { fi->eCount += 1; // token not found, continue continue; } scur = cur; // store this pointer for later use if (*cur == _T(FIELD_CHAR)) { size_t len = _tcslen(tr != NULL ? tr->tszTokenString : fi->tszaTemporaryVars[tmpVarPos]); cur++; if (*(cur + len) != _T(FIELD_CHAR)) { // the next char after the token should be % fi->eCount += 1; continue; } cur += len+1; } else if ( (*cur == _T(FUNC_CHAR)) || (*cur == _T(FUNC_ONCE_CHAR)) ) { TCHAR *argcur; cur += _tcslen(tr->tszTokenString)+1; argcur = getArguments(cur, &argv, &argc); if ( (argcur == cur) || (argcur == NULL) ) { fi->eCount += 1; // error getting arguments continue; } cur = argcur; // arguments for (i=0;iflags&TRF_UNPARSEDARGS)) { afi.tszFormat = argv[i]; afi.eCount = afi.pCount = 0; argv[i] = formatString(&afi); fi->eCount += afi.eCount; fi->pCount += afi.pCount; free(afi.szFormat); } } if (argv[i] == NULL) argv[i] = _tcsdup(_T("")); } } // cur should now point at the character after FIELD_CHAR or after the last ')' if (tr != NULL) { pargv = ( TCHAR** )malloc((argc+1)*sizeof(TCHAR *)); if (pargv == NULL) { fi->eCount += 1; return NULL; } for (i=0;itszTokenString; ZeroMemory(&ai, sizeof(ai)); ai.cbSize = sizeof(ai); ai.argc = argc+1; ai.targv = pargv; ai.fi = fi; if ( (*scur == _T(FUNC_ONCE_CHAR)) || (*scur == _T(FIELD_CHAR))) ai.flags |= AIF_DONTPARSE; parsedToken = parseFromRegister(&ai); free(pargv); } else parsedToken = fi->tszaTemporaryVars[tmpVarPos + 1]; if (parsedToken == NULL) { fi->eCount += 1; continue; } //replaced a var if ( ai.flags & AIF_FALSE ) fi->eCount++; else fi->pCount++; // 'special' chars need to be taken care of (DONTPARSE, TRYPARSE, \r\n) // if the var contains the escape character, this character must be doubled, we don't want it to act as an esacpe char /*for (tcur=parsedToken;*tcur != '\0';tcur++) { if (*tcur == DONTPARSE_CHAR) {//|| (*(var+pos) == ')') ) { parsedToken = realloc(parsedToken, strlen(parsedToken) + 2); if (parsedToken == NULL) { fi->err = EMEM; return NULL; } CopyMemory(tcur+1, tcur, strlen(tcur)+1); tcur++; } }*/ parsedTokenLen = _tcslen(parsedToken); initStrLen = _tcslen(string); tokenLen = cur-scur; scurPos = scur-string; curPos = cur-string; if (tokenLen < parsedTokenLen) { // string needs more memory string = ( TCHAR* )realloc(string, (initStrLen-tokenLen+parsedTokenLen+1)*sizeof(TCHAR)); if (string == NULL) { fi->eCount += 1; return NULL; } } scur = string+scurPos; cur = string+curPos; MoveMemory(scur + parsedTokenLen, cur, (_tcslen(cur)+1)*sizeof(TCHAR)); CopyMemory(scur, parsedToken, parsedTokenLen*sizeof(TCHAR)); { int len; len = _tcslen(string); string = ( TCHAR* )realloc(string, (len+1)*sizeof(TCHAR)); } if (( ai.flags & AIF_DONTPARSE ) || tmpVarPos >= 0) pos += parsedTokenLen; pos--; // parse the same pos again, it changed if ( tr == NULL ) parsedToken = NULL; // To avoid free } if (parsedToken != NULL) free(parsedToken); for ( i=0; i < argc; i++ ) if ( argv[i] != NULL ) free( argv[i] ); if ( argv != NULL ) free(argv); return ( TCHAR* )realloc(string, (_tcslen(string)+1)*sizeof(TCHAR)); } /* MS_VARS_FORMATSTRING */ static INT_PTR formatStringService(WPARAM wParam, LPARAM lParam) { int res, i; BOOL copied; FORMATINFO *fi, tempFi; FORMATINFOV1 *fiv1; TCHAR *tszFormat, *orgFormat, *tszSource, *orgSource, *tRes; if (((FORMATINFO *)wParam)->cbSize >= sizeof(FORMATINFO)) { ZeroMemory(&tempFi, sizeof(FORMATINFO)); CopyMemory(&tempFi, (FORMATINFO *)wParam, sizeof(FORMATINFO)); fi = &tempFi; } else if (((FORMATINFO *)wParam)->cbSize == FORMATINFOV2_SIZE) { ZeroMemory(&tempFi, sizeof(FORMATINFO)); CopyMemory(&tempFi, (FORMATINFO *)wParam, FORMATINFOV2_SIZE); fi = &tempFi; } else { // old struct, must be ANSI fiv1 = (FORMATINFOV1 *)wParam; ZeroMemory(&tempFi, sizeof(FORMATINFO)); tempFi.cbSize = sizeof(FORMATINFO); tempFi.hContact = fiv1->hContact; tempFi.szFormat = fiv1->szFormat; tempFi.szExtraText = fiv1->szSource; fi = &tempFi; } orgFormat = fi->tszFormat; orgSource = fi->tszExtraText; #ifdef UNICODE if (!(fi->flags&FIF_TCHAR)) { copied = TRUE; log_debugA("a2u (%s)", fi->szExtraText); tszFormat = fi->szFormat!=NULL?a2u(fi->szFormat):NULL; tszSource = fi->szExtraText!=NULL?a2u(fi->szExtraText):NULL; for(i = 0; i < fi->cbTemporaryVarsSize; i++) { fi->tszaTemporaryVars[i] = fi->szaTemporaryVars[i]!=NULL?a2u(fi->szaTemporaryVars[i]):NULL; } } else { copied = FALSE; tszFormat = fi->tszFormat; tszSource = fi->tszExtraText; } #else if (fi->flags&FIF_UNICODE) { if (!bWarningShown) { MessageBoxA(NULL, "A plugin using UNICODE encoding tries to make use of the ANSI version of Variables.\r\nPlease use the UNICODE version of Variables or the ANSI version of the plugin using Variables.", "Variables", MB_OK); bWarningShown = TRUE; } return (int)NULL; } copied = FALSE; tszFormat = fi->szFormat; tszSource = fi->szExtraText; #endif fi->tszFormat = tszFormat; fi->tszExtraText = tszSource; tRes = formatString(fi); #ifdef UNICODE if (!(fi->flags&FIF_TCHAR)) { res = (int)u2a(tRes); free(tRes); } else { res = (int)tRes; } #else res = (int)tRes; #endif if (copied) { if (tszFormat != NULL) { free(tszFormat); } if (tszSource != NULL) { free(tszSource); } for(i = 0; i < fi->cbTemporaryVarsSize; i++) { if (fi->tszaTemporaryVars != NULL) { free(fi->tszaTemporaryVars); } } } //fi->tszFormat = orgFormat; //fi->tszExtraText = orgSource; if (((FORMATINFO *)wParam)->cbSize == sizeof(FORMATINFOV1)) { ((FORMATINFOV1 *)wParam)->eCount = fi->eCount; ((FORMATINFOV1 *)wParam)->pCount = fi->pCount; } else { ((FORMATINFO *)wParam)->eCount = fi->eCount; ((FORMATINFO *)wParam)->pCount = fi->pCount; } // clearVariableRegister();? return res; } TCHAR *formatString(FORMATINFO *fi) { /* the service to format a given string */ TCHAR *string, *formattedString; if (fi->eCount + fi->pCount > 5000) { fi->eCount += 1; fi->pCount += 1; log_debugA("Variables: Overflow protection; %d parses", fi->eCount + fi->pCount); return NULL; } if ((fi == NULL) || (fi->tszFormat == NULL)) { return NULL; } string = _tcsdup(fi->tszFormat); if (string == NULL) { return NULL; } formattedString = replaceDynVars(string, fi); free(string); if (formattedString == NULL) { return NULL; } return formattedString; } /* MS_VARS_FREEMEMORY */ static INT_PTR freeMemory(WPARAM wParam, LPARAM lParam) { if ((void*)wParam == NULL) { return -1; } free((void*)wParam); return 0; } /* MS_VARS_GET_MMI this code is copied from Miranda's core (miranda.c) */ INT_PTR getMemoryManagerInterface(WPARAM wParam, LPARAM lParam) { struct MM_INTERFACE *mmi = (struct MM_INTERFACE*) lParam; if (mmi || mmi->cbSize == sizeof(struct MM_INTERFACE)) { mmi->mmi_malloc = malloc; mmi->mmi_realloc = realloc; mmi->mmi_free = free; return 0; } return 1; } int setParseOptions(struct ParseOptions *po) { if (po == NULL) { po = &gParseOpts; } ZeroMemory(po, sizeof(struct ParseOptions)); if (!db_getb(SETTING_STRIPALL, 0)) { po->bStripEOL = db_getb(SETTING_STRIPCRLF, 0); po->bStripWS = db_getb(SETTING_STRIPWS, 0); } else { po->bStripAll = TRUE; } return 0; } int LoadVarModule() { HMODULE hUxTheme; if ( (initTokenRegister() != 0) || (initContactModule() != 0) ) { return -1; } setParseOptions(NULL); hFormatStringService = CreateServiceFunction(MS_VARS_FORMATSTRING, formatStringService); hFreeMemoryService = CreateServiceFunction(MS_VARS_FREEMEMORY, freeMemory); hRegisterVariableService = CreateServiceFunction(MS_VARS_REGISTERTOKEN, registerToken); hGetMMIService = CreateServiceFunction(MS_VARS_GET_MMI, getMemoryManagerInterface); // help dialog hCurSplitNS = LoadCursor(NULL, IDC_SIZENS); hUxTheme = NULL; if(IsWinVerXPPlus()) { hUxTheme = GetModuleHandle(_T("uxtheme.dll")); if (hUxTheme) { pfnEnableThemeDialogTexture = (BOOL (WINAPI *)(HANDLE, DWORD))GetProcAddress(hUxTheme, "EnableThemeDialogTexture"); } } hShowHelpService = CreateServiceFunction(MS_VARS_SHOWHELP, showHelpService); hShowHelpExService = CreateServiceFunction(MS_VARS_SHOWHELPEX, showHelpExService); if (ServiceExists(MS_SKIN2_ADDICON)) { SKINICONDESC sid; char szFile[MAX_PATH]; ZeroMemory(&sid, sizeof(SKINICONDESC)); sid.cbSize = sizeof(SKINICONDESC); sid.ptszSection = TranslateT("Variables"); sid.ptszDescription = TranslateT("Help"); sid.pszName = "vars_help"; GetModuleFileNameA(hInst, szFile, MAX_PATH); sid.pszDefaultFile = szFile; sid.iDefaultIndex = -IDI_V; sid.cx = sid.cy = 16; sid.flags = SIDF_TCHAR; CallService(MS_SKIN2_ADDICON, (WPARAM)0, (LPARAM)&sid); hIconsChangedHook = HookEvent(ME_SKIN2_ICONSCHANGED, iconsChanged); } hGetIconService = CreateServiceFunction(MS_VARS_GETSKINITEM, getSkinItemService); hOptionsHook = HookEvent(ME_OPT_INITIALISE, OptionsInit); // register internal tokens registerExternalTokens(); registerLogicTokens(); registerMathTokens(); registerMirandaTokens(); registerStrTokens(); registerSystemTokens(); registerVariablesTokens(); registerRegExpTokens(); registerInetTokens(); registerXsltTokens(); registerAliasTokens(); registerMetaContactsTokens(); log_debugA("Variables: Internal tokens registered"); if (db_getb(SETTING_PARSEATSTARTUP, 0)) { FORMATINFO fi; ZeroMemory(&fi, sizeof(fi)); fi.cbSize = sizeof(fi); fi.tszFormat = db_gets(SETTING_STARTUPTEXT, NULL); if (fi.tszFormat != NULL) { freeMemory((WPARAM)formatString(&fi), 0); free(fi.tszFormat); } } log_debugA("Variables: Init done"); return 0; } int UnloadVarModule() { UnhookEvent(hOptionsHook); if (hIconsChangedHook != NULL) UnhookEvent(hIconsChangedHook); DestroyServiceFunction(hRegisterVariableService); DestroyServiceFunction(hFreeMemoryService); DestroyServiceFunction(hFormatStringService); DestroyServiceFunction(hGetMMIService); DestroyServiceFunction(hShowHelpService); DestroyServiceFunction(hShowHelpExService); DestroyServiceFunction(hGetIconService); DestroyCursor(hCurSplitNS); deinitContactModule(); deInitExternal(); deinitTokenRegister(); unregisterAliasTokens(); unregisterVariablesTokens(); return 0; }