From bacf5e0272c566c30e591152084daf2f684aebe8 Mon Sep 17 00:00:00 2001 From: Goraf Date: Fri, 5 Feb 2016 22:40:46 +0000 Subject: ContextHelp: initial commit (adopted) * working * 32/64-bit compilable git-svn-id: http://svn.miranda-ng.org/main/trunk@16225 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/ContextHelp/src/helppack.cpp | 520 +++++++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 plugins/ContextHelp/src/helppack.cpp (limited to 'plugins/ContextHelp/src/helppack.cpp') diff --git a/plugins/ContextHelp/src/helppack.cpp b/plugins/ContextHelp/src/helppack.cpp new file mode 100644 index 0000000000..c8a21105aa --- /dev/null +++ b/plugins/ContextHelp/src/helppack.cpp @@ -0,0 +1,520 @@ +/* +Miranda IM Help Plugin +Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath + +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 (Help-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "stdafx.h" + + +/**************************** LOAD PACK ***************************/ + +void TrimStringSimple(char *str) +{ + if (str[lstrlenA(str) - 1] == '\n') + str[lstrlenA(str) - 1] = '\0'; + if (str[lstrlenA(str) - 1] == '\r') + str[lstrlenA(str) - 1] = '\0'; +} + +void TrimString(char *str) +{ + int len, start; + len = lstrlenA(str); + while (str[0] != '\0' && ((unsigned char)str[len - 1] <= ' ')) + str[--len] = 0; + for (start = 0; str[start] && ((unsigned char)str[start] <= ' '); start++); + MoveMemory(str, str + start, len - start + 1); +} + +BOOL IsEmpty(const char *str) +{ + int i; + for (i = 0; str[i] != '\0'; i++) + if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') + return FALSE; + return TRUE; +} + +static void CleanupLanguage(char *szLanguage, LCID locale) +{ + char *p; + if (PRIMARYLANGID(LANGIDFROMLCID(locale)) != LANG_ENGLISH) { + /* remove any appended ' (default)' */ + p = strstr(szLanguage, " (default)"); + if (p != NULL) + *p = '\0'; + } +} + +static void CleanupAuthors(char *szAuthors) +{ + char *p, *p2; + /* remove trailing dot (if any) */ + p = &szAuthors[lstrlenA(szAuthors) - 1]; + if (*p == '.') + *p = '\0'; + /* remove any extra info in parentheses, which is ok + * but makes the list very long for some packs */ + for (;;) { + p = strchr(szAuthors, '('); + p2 = strchr(szAuthors, ')'); + if (p == NULL || p2 == NULL) { + p = strchr(szAuthors, '['); + p2 = strchr(szAuthors, ']'); + if (p == NULL || p2 == NULL) + break; + } + if (*(p - 1) == ' ') + --p; + MoveMemory(p, p2 + 1, lstrlenA(p2 + 1) + 1); + } +} + +static void CleanupEmail(char *szAuthorEmail) +{ + char c, *p, *pAt; + /* replace ' dot ' with '.' (may be removed) */ + p = strstr(szAuthorEmail, " dot "); + if (p != NULL) { + *p = '.'; + MoveMemory(p + 1, p + 5, lstrlenA(p + 5) + 1); + } + /* also allow ' at ' instead of '@' for obfuscation */ + p = strstr(szAuthorEmail, " at "); + if (p != NULL) { + *p = '@'; + MoveMemory(p + 1, p + 4, lstrlenA(p + 4) + 1); + } + /* is valid? */ + pAt = strchr(szAuthorEmail, '@'); + if (pAt == NULL) { + szAuthorEmail[0] = '\0'; + return; + } + /* strip-off extra text except exactly one email address + * this is needed as a click on the email addres brings up the mail client */ + for (c = ' ';; c = ',') { + p = strchr(pAt, c); + if (p != NULL) + *p = '\0'; + p = strrchr(szAuthorEmail, c); + if (p != NULL) + MoveMemory(szAuthorEmail, p + 1, lstrlenA(p + 1) + 1); + if (c == ',') + break; + } + p = strstr(szAuthorEmail, "__"); + if (p != NULL) + MoveMemory(szAuthorEmail, p + 2, lstrlenA(p + 2) + 1); + /* lower case */ + CharLowerA(szAuthorEmail); + /* 'none' specified */ + if (!lstrcmpiA(szAuthorEmail, "none")) + szAuthorEmail[0] = '\0'; +} + +static void CleanupLastModifiedUsing(char *szLastModifiedUsing, int nSize) +{ + char *p; + /* remove 'Unicode', as it doesn't matter */ + p = strstr(szLastModifiedUsing, " Unicode"); + if (p != NULL) MoveMemory(p, p + 8, lstrlenA(p + 8) + 1); + /* use 'Miranda IM' instead of 'Miranda' */ + p = strstr(szLastModifiedUsing, "Miranda"); + if (p != NULL && strncmp(p + 7, " IM", 3)) { + MoveMemory(p + 10, p + 7, lstrlenA(p + 7) + 1); + CopyMemory(p + 7, " IM", 3); + } + /* use 'Plugin' instead of 'plugin' */ + p = strstr(szLastModifiedUsing, " plugin"); + if (p != NULL) + CopyMemory(p, " Plugin", 7); + /* remove 'v' prefix */ + p = strstr(szLastModifiedUsing, " v0."); + if (p != NULL) + MoveMemory(p + 1, p + 2, lstrlenA(p + 2) + 1); + /* default if empty */ + if (!szLastModifiedUsing[0]) { + lstrcpynA(szLastModifiedUsing, MIRANDANAME" ", nSize); + CallService(MS_SYSTEM_GETVERSIONTEXT, nSize - lstrlenA(szLastModifiedUsing), (LPARAM)szLastModifiedUsing + lstrlenA(szLastModifiedUsing)); + } +} + +// pack struct should be initialized to zero before call +// pack->szFileName needs to be filled in before call +static BOOL LoadPackData(HELPPACK_INFO *pack, BOOL fEnabledPacks, const char *pszFileVersionHeader) +{ + FILE *fp; + TCHAR szFileName[MAX_PATH]; + char line[4096], *pszColon, *buf; + char szLanguageA[64]; /* same size as pack->szLanguage */ + /* + Miranda Help Pack Version 1 + Language: (optional) + Locale: 0809 + Authors: Miranda NG Development Team (multiple tags allowed) + Author-email: project-info at miranda-ng.org (" at " instead of "@" allowed) + Last-Modified-Using: Miranda IM 0.7 + Plugins-included: (multiple tags allowed) + X-FLName: name as used on the file listing (non-standard extension) + X-Version: 1.2.3.4 (non-standard extension) + see 'Help-Translation.txt' for some header quidelines + */ + if (!GetPackPath(szFileName, sizeof(szFileName), fEnabledPacks, pack->szFileName)) + return FALSE; + fp = _tfopen(szFileName, _T("rt")); + if (fp == NULL) + return FALSE; + fgets(line, sizeof(line), fp); + TrimString(line); + if (lstrcmpA(line, pszFileVersionHeader)) { + fclose(fp); + return FALSE; + } + pack->flags = HPF_NOLOCALE; + pack->Locale = LOCALE_USER_DEFAULT; + szLanguageA[0] = '\0'; + while (!feof(fp)) { + if (fgets(line, sizeof(line), fp) == NULL) + break; + TrimString(line); + if (IsEmpty(line) || line[0] == ';' || line[0] == '\0') + continue; + if (line[0] == '[') + break; + pszColon = strchr(line, ':'); + if (pszColon == NULL) + continue; + *pszColon = '\0'; + TrimString(pszColon + 1); + if (!lstrcmpA(line, "Language") && !pack->szLanguage[0]) + lstrcpynA(szLanguageA, pszColon + 1, sizeof(szLanguageA)); /* buffer safe */ + else if (!lstrcmpA(line, "Last-Modified-Using") && !pack->szLastModifiedUsing[0]) + lstrcpynA(pack->szLastModifiedUsing, pszColon + 1, sizeof(pack->szLastModifiedUsing)); /* buffer safe */ + else if (!lstrcmpA(line, "Authors")) { + buf = pack->szAuthors + lstrlenA(pack->szAuthors); /* allow multiple tags */ + if ((sizeof(pack->szAuthors) - lstrlenA(pack->szAuthors))>0) /* buffer safe */ + mir_snprintf(buf, sizeof(pack->szAuthors) - lstrlenA(pack->szAuthors), (pack->szAuthors[0] == '\0') ? "%s" : " %s", pszColon + 1); + } + else if (!lstrcmpA(line, "Author-email") && !pack->szAuthorEmail[0]) + lstrcpynA(pack->szAuthorEmail, pszColon + 1, sizeof(pack->szAuthorEmail)); /* buffer safe */ + else if (!lstrcmpA(line, "Locale") && pack->flags&HPF_NOLOCALE) { + pack->Locale = MAKELCID((USHORT)strtol(pszColon + 1, NULL, 16), SORT_DEFAULT); + if (pack->Locale) + pack->flags &= ~HPF_NOLOCALE; + } + else if (!lstrcmpA(line, "Plugins-included")) { + buf = pack->szPluginsIncluded + lstrlenA(pack->szPluginsIncluded); /* allow multiple tags */ + if ((sizeof(pack->szPluginsIncluded) - lstrlenA(pack->szPluginsIncluded))>0) /* buffer safe */ + mir_snprintf(buf, sizeof(pack->szPluginsIncluded) - lstrlenA(pack->szPluginsIncluded), (pack->szPluginsIncluded[0] == '\0') ? "%s" : ", %s", CharLowerA(pszColon + 1)); + } + else if (!lstrcmpA(line, "X-Version") && !pack->szVersion[0]) + lstrcpynA(pack->szVersion, pszColon + 1, sizeof(pack->szVersion)); /* buffer safe */ + else if (!lstrcmpA(line, "X-FLName") && !pack->szFLName[0]) + lstrcpynA(pack->szFLName, pszColon + 1, sizeof(pack->szFLName)); /* buffer safe */ + } + /* default */ + if (PRIMARYLANGID(LANGIDFROMLCID(pack->Locale)) == LANG_ENGLISH && strstr(szLanguageA, " (default)") != NULL) + pack->flags |= HPF_DEFAULT; + CleanupLanguage(szLanguageA, pack->Locale); + CleanupAuthors(pack->szAuthors); + CleanupEmail(pack->szAuthorEmail); + CleanupLastModifiedUsing(pack->szLastModifiedUsing, sizeof(pack->szLastModifiedUsing)); + /* codepage */ + if (!(pack->flags&HPF_NOLOCALE)) + if (GetLocaleInfoA(pack->Locale, LOCALE_IDEFAULTANSICODEPAGE, line, 6)) + pack->codepage = (WORD)atoi(line); /* CP_ACP on error */ + /* language */ + MultiByteToWideChar(pack->codepage, 0, szLanguageA, -1, pack->szLanguage, sizeof(pack->szLanguage)); + /* ensure the pack always has a language name */ + if (!pack->szLanguage[0] && !GetLocaleInfo(pack->Locale, LOCALE_SENGLANGUAGE, pack->szLanguage, sizeof(pack->szLanguage))) { + TCHAR *p; + lstrcpyn(pack->szLanguage, pack->szFileName, sizeof(pack->szLanguage)); /* buffer safe */ + p = _tcsrchr(pack->szLanguage, _T('.')); + if (p != NULL) + *p = '\0'; + } + /* ensure the pack always has a filelisting name */ + if (!pack->szFLName[0]) + lstrcatA(lstrcpyA(pack->szFLName, szLanguageA), " Help Pack"); /* buffer safe */ + fclose(fp); + + return TRUE; +} + +/**************************** ENUM PACKS **************************/ + +BOOL GetPackPath(TCHAR *pszPath, int nSize, BOOL fEnabledPacks, const TCHAR *pszFile) +{ + TCHAR *p; + /* main path */ + if (!GetModuleFileName(NULL, pszPath, nSize)) + return FALSE; + p = _tcsrchr(pszPath, _T('\\')); + if (p != NULL) + *(p + 1) = _T('\0'); + /* subdirectory */ + if (!fEnabledPacks) { + if (nSize<(lstrlen(pszPath) + 10)) + return FALSE; + lstrcat(pszPath, _T("Languages\\")); + } + /* file name */ + if (pszFile != NULL) { + if (nSize < (lstrlen(pszFile) + 11)) + return FALSE; + lstrcat(pszPath, pszFile); + } + + return TRUE; +} + +// callback is allowed to be NULL +// returns TRUE if any pack exists except default +BOOL EnumPacks(ENUM_PACKS_CALLBACK callback, const TCHAR *pszFilePattern, const char *pszFileVersionHeader, WPARAM wParam, LPARAM lParam) +{ + BOOL fPackFound = FALSE; + BOOL res = FALSE; + HELPPACK_INFO pack; + WIN32_FIND_DATA wfd; + HANDLE hFind; + + /* enabled packs */ + if (GetPackPath(pack.szFileName, sizeof(pack.szFileName), TRUE, pszFilePattern)) { + hFind = FindFirstFile(pack.szFileName, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + continue; + if ((lstrlen(wfd.cFileName) < 4) || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')) + continue; + + /* get data */ + ZeroMemory(&pack, sizeof(pack)); + lstrcpy(pack.szFileName, CharLower(wfd.cFileName)); /* buffer safe */ + if (LoadPackData(&pack, TRUE, pszFileVersionHeader)) { + pack.ftFileDate = wfd.ftLastWriteTime; + + /* enabled? */ + if (!fPackFound) + pack.flags |= HPF_ENABLED; + fPackFound = TRUE; + + /* callback */ + if (callback != NULL) + res = callback(&pack, wParam, lParam); + if (!res) { + FindClose(hFind); + return FALSE; + } + } + } while (FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + } + + /* disabled packs */ + if (GetPackPath(pack.szFileName, sizeof(pack.szFileName), FALSE, pszFilePattern)) { + hFind = FindFirstFile(pack.szFileName, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + continue; + if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')) + continue; + + /* get data */ + ZeroMemory(&pack, sizeof(pack)); + lstrcpy(pack.szFileName, CharLower(wfd.cFileName)); /* buffer safe */ + if (LoadPackData(&pack, FALSE, pszFileVersionHeader)) { + pack.ftFileDate = wfd.ftLastWriteTime; + fPackFound = TRUE; + + /* callback */ + if (callback != NULL) + res = callback(&pack, wParam, lParam); + if (!res) { + FindClose(hFind); + return FALSE; + } + } + } while (FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + } + + return fPackFound; +} + +BOOL IsPluginIncluded(const HELPPACK_INFO *pack, char *pszFileBaseName) +{ + char *p; + if (!lstrcmpiA(pszFileBaseName, "png2dib") || !lstrcmpiA(pszFileBaseName, "loadavatars")) + return TRUE; /* workaround: does not need no translation */ + for (p = (char*)pack->szPluginsIncluded;;) { + p = strstr(p, CharLowerA(pszFileBaseName)); + if (p == NULL) + return FALSE; + if (p == pack->szPluginsIncluded || *(p - 1) == ' ' || *(p - 1) == ',') { + p += lstrlenA(pszFileBaseName) + 1; + if (*p == ',' || *p == ' ' || *p == 0) + return TRUE; + } + else + p += lstrlenA(pszFileBaseName) + 1; + } + + return FALSE; +} + +/**************************** SWITCH PACKS ************************/ + +BOOL EnablePack(const HELPPACK_INFO *pack, const TCHAR *pszFilePattern) +{ + TCHAR szFrom[MAX_PATH], szDest[MAX_PATH]; + + /* disable previous pack */ + if (GetPackPath(szFrom, sizeof(szFrom), TRUE, pszFilePattern)) { + WIN32_FIND_DATA wfd; + HANDLE hFind = FindFirstFile(szFrom, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + continue; + if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')) + continue; + /* ensure dir exists */ + if (GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL)) + CreateDirectory(szFrom, NULL); + /* move file */ + if (GetPackPath(szFrom, sizeof(szFrom), TRUE, wfd.cFileName)) + if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) + if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) { + DeleteFile(szDest); + MoveFile(szFrom, szDest); + } + break; + } while (FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + } + + /* enable current pack */ + if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pack->szFileName)) + if (GetPackPath(szDest, sizeof(szDest), TRUE, pack->szFileName)) + return MoveFile(szFrom, szDest); + + return FALSE; +} + +void CorrectPacks(const TCHAR *pszFilePattern, const TCHAR *pszDefaultFile, BOOL fDisableAll) +{ + TCHAR szFrom[MAX_PATH], szDest[MAX_PATH], szDir[MAX_PATH], *pszFile; + BOOL fDirCreated = FALSE, fOneEnabled = FALSE; + HANDLE hFind; + WIN32_FIND_DATA wfd; + + /* main path */ + if (!GetModuleFileName(NULL, szDir, sizeof(szDir))) + return; + pszFile = _tcsrchr(szDir, _T('\\')); + if (pszFile != NULL) + *pszFile = _T('\0'); + + /* move wrongly placed packs from 'Plugins' to 'Language' */ + mir_sntprintf(szFrom, sizeof(szFrom), _T("%s\\Plugins\\%s"), szDir, pszFilePattern); + hFind = FindFirstFile(szFrom, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + continue; + if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')) + continue; + + /* ensure dir exists */ + if (!fDirCreated && GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL)) + fDirCreated = CreateDirectory(szFrom, NULL); + + /* move file */ + if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) { + mir_sntprintf(szFrom, sizeof(szFrom), _T("%s\\Plugins\\%s"), szDir, wfd.cFileName); + if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) { + DeleteFile(szDest); + MoveFile(szFrom, szDest); + } + } + } while (FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + + /* disable all packs except one */ + if (GetPackPath(szFrom, sizeof(szFrom), TRUE, pszFilePattern)) { + hFind = FindFirstFile(szFrom, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + continue; + if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')) + continue; + /* skip first file */ + fOneEnabled = TRUE; + if (!fDisableAll) { + fDisableAll = TRUE; + continue; + } + /* ensure dir exists */ + if (!fDirCreated && GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL)) + fDirCreated = CreateDirectory(szFrom, NULL); + /* move file */ + if (GetPackPath(szFrom, sizeof(szFrom), TRUE, wfd.cFileName)) + if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) { + if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) { + DeleteFile(szDest); + MoveFile(szFrom, szDest); + } + } + } while (FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + } + + /* ensure one is enabled if installed */ + if (!fOneEnabled) { + /* try to move english */ + if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pszDefaultFile)) + if (GetPackPath(szDest, sizeof(szDest), TRUE, pszDefaultFile)) + fOneEnabled = MoveFile(szFrom, szDest); + /* fallback on other one */ + if (!fOneEnabled) + if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pszFilePattern)) { + hFind = FindFirstFile(szFrom, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + continue; + if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')) + continue; + /* move first file */ + if (GetPackPath(szFrom, sizeof(szFrom), FALSE, wfd.cFileName)) + if (GetPackPath(szDest, sizeof(szDest), TRUE, wfd.cFileName)) + MoveFile(szFrom, szDest); + break; + } while (FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + } + } +} -- cgit v1.2.3