From ebd0852b60cc002d5451c559215b5c837ff18f13 Mon Sep 17 00:00:00 2001 From: kichik Date: Sat, 31 Jan 2004 19:54:45 +0000 Subject: [PATCH] fixed mishandling of MBCS chars git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@3441 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/InstallOptions/InstallerOptions.cpp | 247 +++++++++++--------- Contrib/nsExec/nsexec.c | 91 +++++--- Plugins/InstallOptions.dll | Bin 12288 -> 12288 bytes Plugins/nsExec.dll | Bin 6144 -> 6144 bytes 4 files changed, 193 insertions(+), 145 deletions(-) diff --git a/Contrib/InstallOptions/InstallerOptions.cpp b/Contrib/InstallOptions/InstallerOptions.cpp index 32563131..b1e90ed3 100644 --- a/Contrib/InstallOptions/InstallerOptions.cpp +++ b/Contrib/InstallOptions/InstallerOptions.cpp @@ -54,6 +54,16 @@ char *WINAPI STRDUP(const char *c) return lstrcpy(t,c); } +// Turn a pair of chars into a word +// Turn four chars into a dword +#ifdef __BIG_ENDIAN__ // Not very likely, but, still... +#define CHAR2_TO_WORD(a,b) (((WORD)(b))|((a)<<8)) +#define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(c,d))|(CHAR2_TO_WORD(a,b)<<16)) +#else +#define CHAR2_TO_WORD(a,b) (((WORD)(a))|((b)<<8)) +#define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(a,b))|(CHAR2_TO_WORD(c,d)<<16)) +#endif + // Field types // NB - the order of this list is important - see below @@ -120,6 +130,9 @@ int WINAPI LookupTokens(TableEntry*, char*); void WINAPI ConvertNewLines(char *str); +// all allocated buffers must be first in the struct +// when adding more allocated buffers to FieldType, don't forget to change this define +#define FIELD_BUFFERS 6 struct FieldType { char *pszText; char *pszState; @@ -128,12 +141,12 @@ struct FieldType { char *pszListItems; char *pszFilter; - int nType; - RECT rect; - + char *pszValidateText; int nMinLength; int nMaxLength; - char *pszValidateText; + + int nType; + RECT rect; int nFlags; @@ -313,28 +326,30 @@ bool WINAPI SaveSettings(void) { pszBuffer[nLength+1]='"'; pszBuffer[nLength+2]='\0'; - if ( pField->nType == FIELD_TEXT && (pField->nFlags & FLAG_MULTILINE) ) + if (pField->nType == FIELD_TEXT && (pField->nFlags & FLAG_MULTILINE)) { char *pszBuf2 = (char*)MALLOC(nBufLen*2); // double the size, consider the worst case, all chars are \r\n char *p1, *p2; - for (p1=pszBuffer,p2=pszBuf2; *p1; p1++, p2++) { + for (p1 = pszBuffer, p2 = pszBuf2; *p1; p1 = CharNext(p1), p2 = CharNext(p2)) + { switch (*p1) { case '\t': - *p2++ = '\\'; - *p2 = 't'; + *(LPWORD)p2 = CHAR2_TO_WORD('\\', 't'); + p2++; break; case '\n': - *p2++ = '\\'; - *p2 = 'n'; + *(LPWORD)p2 = CHAR2_TO_WORD('\\', 'n'); + p2++; break; case '\r': - *p2++ = '\\'; - *p2 = 'r'; + *(LPWORD)p2 = CHAR2_TO_WORD('\\', 'r'); + p2++; break; case '\\': *p2++ = '\\'; default: - *p2=*p1; + lstrcpyn(p2, p1, CharNext(p1) - p1 + 1); + break; } } *p2 = 0; @@ -361,46 +376,48 @@ bool WINAPI SaveSettings(void) { #define BROWSE_WIDTH 15 static char szResult[BUFFER_SIZE]; +char *pszAppName; -DWORD WINAPI myGetProfileString(LPCTSTR lpAppName, LPCTSTR lpKeyName) +DWORD WINAPI myGetProfileString(LPCTSTR lpKeyName) { *szResult = '\0'; - return GetPrivateProfileString(lpAppName, lpKeyName, "", szResult, BUFFER_SIZE, pszFilename); + return GetPrivateProfileString(pszAppName, lpKeyName, "", szResult, BUFFER_SIZE, pszFilename); } -char * WINAPI myGetProfileStringDup(LPCTSTR lpAppName, LPCTSTR lpKeyName) +char * WINAPI myGetProfileStringDup(LPCTSTR lpKeyName) { - int nSize = myGetProfileString(lpAppName, lpKeyName); - if ( nSize ) + int nSize = myGetProfileString(lpKeyName); + if (nSize) return strdup(szResult); else return NULL; } -UINT WINAPI myGetProfileInt(LPCTSTR lpAppName, LPCTSTR lpKeyName, INT nDefault) +UINT WINAPI myGetProfileInt(LPCTSTR lpKeyName, INT nDefault) { - return GetPrivateProfileInt(lpAppName, lpKeyName, nDefault, pszFilename); + return GetPrivateProfileInt(pszAppName, lpKeyName, nDefault, pszFilename); } int WINAPI ReadSettings(void) { static char szField[25]; int nIdx, nCtrlIdx; - pszTitle = myGetProfileStringDup("Settings", "Title"); - pszCancelButtonText = myGetProfileStringDup("Settings", "CancelButtonText"); - pszNextButtonText = myGetProfileStringDup("Settings", "NextButtonText"); - pszBackButtonText = myGetProfileStringDup("Settings", "BackButtonText"); + pszAppName = "Settings"; + pszTitle = myGetProfileStringDup("Title"); + pszCancelButtonText = myGetProfileStringDup("CancelButtonText"); + pszNextButtonText = myGetProfileStringDup("NextButtonText"); + pszBackButtonText = myGetProfileStringDup("BackButtonText"); - nNumFields = myGetProfileInt("Settings", "NumFields", 0); + nNumFields = myGetProfileInt("NumFields", 0); - nRectId = myGetProfileInt("Settings", "Rect", DEFAULT_RECT); + nRectId = myGetProfileInt("Rect", DEFAULT_RECT); - bBackEnabled = myGetProfileInt("Settings", "BackEnabled", -1); + bBackEnabled = myGetProfileInt("BackEnabled", -1); // by ORTIM: 13-August-2002 - bCancelEnabled = myGetProfileInt("Settings", "CancelEnabled", -1); - bCancelShow = myGetProfileInt("Settings", "CancelShow", -1); + bCancelEnabled = myGetProfileInt("CancelEnabled", -1); + bCancelShow = myGetProfileInt("CancelShow", -1); - bRTL = myGetProfileInt("Settings", "RTL", 0); + bRTL = myGetProfileInt("RTL", 0); if (nNumFields > 0) { // make this twice as large for the worst case that every control is a browse button. @@ -464,25 +481,26 @@ int WINAPI ReadSettings(void) { FieldType *pField = pFields + nIdx; wsprintf(szField, "Field %d", nCtrlIdx + 1); + pszAppName = szField; // Get the control type - myGetProfileString(szField, "TYPE"); + myGetProfileString("TYPE"); pField->nType = LookupToken(TypeTable, szResult); if (pField->nType == FIELD_INVALID) continue; // Lookup flags associated with the control type pField->nFlags = LookupToken(FlagTable, szResult); - myGetProfileString(szField, "Flags"); + myGetProfileString("Flags"); pField->nFlags |= LookupTokens(FlagTable, szResult); // pszState must not be NULL! - myGetProfileString(szField, "State"); + myGetProfileString("State"); pField->pszState = strdup(szResult); // ListBox items list { - int nResult = myGetProfileString(szField, "ListItems"); + int nResult = myGetProfileString("ListItems"); if (nResult) { // add an extra | character to the end to simplify the loop where we add the items. pField->pszListItems = (char*)MALLOC(nResult + 2); @@ -493,15 +511,15 @@ int WINAPI ReadSettings(void) { } // Label Text - convert newline - pField->pszText = myGetProfileStringDup(szField, "TEXT"); + pField->pszText = myGetProfileStringDup("TEXT"); if (pField->nType == FIELD_LABEL) ConvertNewLines(pField->pszText); // Dir request - root folder - pField->pszRoot = myGetProfileStringDup(szField, "ROOT"); + pField->pszRoot = myGetProfileStringDup("ROOT"); // ValidateText - convert newline - pField->pszValidateText = myGetProfileStringDup(szField, "ValidateText"); + pField->pszValidateText = myGetProfileStringDup("ValidateText"); ConvertNewLines(pField->pszValidateText); { @@ -512,22 +530,25 @@ int WINAPI ReadSettings(void) { pField->pszFilter = (char*)MALLOC(nResult + 2); strcpy(pField->pszFilter, szResult); char *pszPos = pField->pszFilter; - while (*pszPos) { - if (*pszPos == '|') *pszPos = '\0'; - pszPos++; + while (*pszPos) + { + if (*pszPos == '|') + *pszPos++ = 0; + else + pszPos = CharNext(pszPos); } } } - pField->rect.left = myGetProfileInt(szField, "LEFT", 0); - pField->rect.top = myGetProfileInt(szField, "TOP", 0); - pField->rect.right = myGetProfileInt(szField, "RIGHT", 0); - pField->rect.bottom = myGetProfileInt(szField, "BOTTOM", 0); - pField->nMinLength = myGetProfileInt(szField, "MinLen", 0); - pField->nMaxLength = myGetProfileInt(szField, "MaxLen", 0); + pField->rect.left = myGetProfileInt("LEFT", 0); + pField->rect.top = myGetProfileInt("TOP", 0); + pField->rect.right = myGetProfileInt("RIGHT", 0); + pField->rect.bottom = myGetProfileInt("BOTTOM", 0); + pField->nMinLength = myGetProfileInt("MinLen", 0); + pField->nMaxLength = myGetProfileInt("MaxLen", 0); // Text color for LINK control, default is pure blue - pField->hImage = (HANDLE)myGetProfileInt(szField, "TxtColor", RGB(0,0,255)); + pField->hImage = (HANDLE)myGetProfileInt("TxtColor", RGB(0,0,255)); pField->nControlID = 1200 + nIdx; if (pField->nType == FIELD_FILEREQUEST || pField->nType == FIELD_DIRREQUEST) @@ -1141,42 +1162,38 @@ int WINAPI createCfgDlg() } char *pszStart, *pszEnd, *pszList; pszStart = pszEnd = pszList = STRDUP(pField->pszListItems); - while ((*pszEnd) && (*pszStart)) { + // pszListItems has a trailing pipe + while (*pszEnd) { if (*pszEnd == '|') { *pszEnd = '\0'; - if (pszEnd > pszStart) { - mySendMessage(hwCtrl, nAddMsg, 0, (LPARAM)pszStart); - } - // jump to the next item, skip any redundant | characters - do { pszEnd++; } while (*pszEnd == '|'); - pszStart = pszEnd; + if (*pszStart) + mySendMessage(hwCtrl, nAddMsg, 0, (LPARAM) pszStart); + pszStart = ++pszEnd; } else - pszEnd++; + pszEnd = CharNext(pszEnd); } FREE(pszList); if (pField->pszState) { if (pField->nFlags & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && nFindMsg == LB_FINDSTRINGEXACT) { mySendMessage(hwCtrl, LB_SETSEL, FALSE, -1); pszStart = pszEnd = pField->pszState; - while (*pszStart) { - char cLast = *pszEnd; - if (*pszEnd == '|') *pszEnd = '\0'; - if (!*pszEnd) { - if (pszEnd > pszStart) { - int nItem = mySendMessage(hwCtrl, nFindMsg, -1, (LPARAM)pszStart); - if (nItem != CB_ERR) { // CB_ERR == LB_ERR == -1 + for (;;) { + char c = *pszEnd; + if (c == '|' || c == '\0') { + *pszEnd = '\0'; + if (*pszStart) + { + int nItem = mySendMessage(hwCtrl, LB_FINDSTRINGEXACT, -1, (LPARAM)pszStart); + if (nItem != LB_ERR) mySendMessage(hwCtrl, LB_SETSEL, TRUE, nItem); - } } - if (cLast) { - do { - pszEnd++; - } while (*pszEnd == '|'); - } - pszStart = pszEnd; + if (!c) + break; + pszStart = ++pszEnd; } - pszEnd++; + else + pszEnd = CharNext(pszEnd); } } else { @@ -1289,11 +1306,11 @@ void WINAPI showCfgDlg() int i = nNumFields; while (i--) { FieldType *pField = pFields + i; - FREE(pField->pszText); - FREE(pField->pszState); - FREE(pField->pszListItems); - FREE(pField->pszFilter); - FREE(pField->pszRoot); + + int j = FIELD_BUFFERS; + while (j--) + FREE(((char **) pField)[j]); + if (pField->nType == FIELD_BITMAP) { DeleteObject(pField->hImage); } @@ -1352,8 +1369,6 @@ extern "C" void __declspec(dllexport) show(HWND hwndParent, int string_size, extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { m_hInstance=(HINSTANCE) hInst; - if (ul_reason_for_call == DLL_THREAD_DETACH || ul_reason_for_call == DLL_PROCESS_DETACH) - DestroyWindow(hConfigWindow); return TRUE; } @@ -1372,48 +1387,60 @@ int WINAPI LookupTokens(TableEntry* psTable_, char* pszTokens_) char *pszStart = pszTokens_; char *pszEnd = pszTokens_; for (;;) { - if (*pszEnd == '\0') { - n |= LookupToken(psTable_, pszStart); - break; - } - if (*pszEnd == '|') { + char c = *pszEnd; + if (c == '|' || c == '\0') { *pszEnd = '\0'; n |= LookupToken(psTable_, pszStart); - *pszEnd = '|'; - pszStart = pszEnd + 1; + *pszEnd = c; + if (!c) + break; + pszStart = ++pszEnd; } - pszEnd++; + else + pszEnd = CharNext(pszEnd); } return n; } void WINAPI ConvertNewLines(char *str) { - char *p1, *p2; - if (!str) return; - for (p1=p2=str; *p1; p1++, p2++) { - if (*p1 == '\\') { - switch (p1[1]) { - case 't': - *p2 = '\t'; - break; - case 'n': - *p2 = '\n'; - break; - case 'r': - *p2 = '\r'; - break; - case '\\': - *p2 = '\\'; - break; - default: - p1--; - p2--; - break; - } - p1++; + char *p1, *p2, *p3; + + if (!str) + return; + + p1 = p2 = str; + + while (*p1) + { + switch (*(LPWORD)p1) + { + case CHAR2_TO_WORD('\\', 't'): + *p2 = '\t'; + p1 += 2; + p2++; + break; + case CHAR2_TO_WORD('\\', 'n'): + *p2 = '\n'; + p1 += 2; + p2++; + break; + case CHAR2_TO_WORD('\\', 'r'): + *p2 = '\r'; + p1 += 2; + p2++; + break; + case CHAR2_TO_WORD('\\', '\\'): + *p2 = '\\'; + p1 += 2; + p2++; + break; + default: + p3 = CharNext(p1); + while (p1 < p3) + *p2++ = *p1++; + break; } - else *p2 = *p1; } + *p2 = 0; } - diff --git a/Contrib/nsExec/nsexec.c b/Contrib/nsExec/nsexec.c index 21a2b55e..dfa16fad 100644 --- a/Contrib/nsExec/nsexec.c +++ b/Contrib/nsExec/nsexec.c @@ -38,7 +38,7 @@ unsigned int g_to; void ExecScript(BOOL log); void LogMessage(const char *pStr); -char *my_strstr(const char *string, const char *strCharSet); +char *my_strstr(char *a, char *b); unsigned int my_atoi(char *s); void __declspec(dllexport) Exec(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { @@ -94,14 +94,25 @@ void ExecScript(int log) { nComSpecSize = GetModuleFileName(g_hInst, meDLLPath, MAX_PATH); p = meDLLPath + nComSpecSize - 1; g_exec = (char *)GlobalAlloc(GPTR, sizeof(char)*g_stringsize+nComSpecSize+1); - - while ( *p && *p != '\\' ) - p--; - if ( p ) - *p = 0; + + do + { + if (*p == '\\') + break; + p = CharPrev(meDLLPath, p); + } + while (p > meDLLPath); + if (p == meDLLPath) + { + // bad path + lstrcpy(szRet, "error"); + goto done; + } + + *p = 0; GetTempFileName(meDLLPath, "ns", 0, g_exec); *p = '\\'; - if ( CopyFile(meDLLPath, g_exec, FALSE) ) + if (CopyFile(meDLLPath, g_exec, FALSE)) { HANDLE hFile, hMapping; LPBYTE pMapView; @@ -109,7 +120,7 @@ void ExecScript(int log) { hFile = CreateFile(g_exec, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0, 0); hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); pMapView = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0); - if ( pMapView ) + if (pMapView) { pNTHeaders = (PIMAGE_NT_HEADERS)(pMapView + ((PIMAGE_DOS_HEADER)pMapView)->e_lfanew); pNTHeaders->FileHeader.Characteristics = IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_LOCAL_SYMS_STRIPPED | @@ -129,12 +140,12 @@ void ExecScript(int log) { *pExec = ' '; pExec++; popstring(pExec); - if ( my_strstr(pExec, "/TIMEOUT=") ) { + if (my_strstr(pExec, "/TIMEOUT=")) { char *szTimeout = pExec + 9; g_to = my_atoi(szTimeout); popstring(pExec); } - if (!g_exec[0] ) + if (!g_exec[0]) { lstrcpy(szRet, "error"); goto done; @@ -216,30 +227,39 @@ void ExecScript(int log) { if (!(log & 2)) { while (p = my_strstr(p, "\t")) { - int len = lstrlen(p); - char *c_out=(char*)p+TAB_REPLACE_SIZE+len; - char *c_in=(char *)p+len; - while (len-- > 0) { - *c_out--=*c_in--; + if ((int)(p - szUnusedBuf) > (int)(GlobalSize(hUnusedBuf) - TAB_REPLACE_SIZE - 1)) + { + *p++ = ' '; } + else + { + int len = lstrlen(p); + char *c_out=(char*)p+TAB_REPLACE_SIZE+len; + char *c_in=(char *)p+len; + while (len-- > 0) { + *c_out--=*c_in--; + } - lstrcpy(p, TAB_REPLACE); - p += TAB_REPLACE_SIZE; - *p = ' '; + lstrcpy(p, TAB_REPLACE); + p += TAB_REPLACE_SIZE; + *p = ' '; + } } p = szUnusedBuf; // get the old left overs - for (p2 = p; *p2; p2++) { + for (p2 = p; *p2;) { if (*p2 == '\r') { - *p2 = 0; + *p2++ = 0; continue; } if (*p2 == '\n') { *p2 = 0; while (!*p && p != p2) p++; LogMessage(p); - p = p2 + 1; + p = ++p2; + continue; } + p2 = CharNext(p2); } // If data was taken out from the unused buffer, move p contents to the start of szUnusedBuf @@ -299,21 +319,22 @@ void LogMessage(const char *pStr) { ListView_EnsureVisible(g_hwndList, item.iItem, 0); } - -char *my_strstr(const char *string, const char *strCharSet) { - char *s1, *s2; - size_t chklen; - size_t i; - if (lstrlen(string) < lstrlen(strCharSet)) return 0; - if (!*strCharSet) return (char*)string; - chklen=lstrlen(string)-lstrlen(strCharSet); - for (i = 0; i <= chklen; i++) { - s1=&((char*)string)[i]; - s2=(char*)strCharSet; - while (*s1++ == *s2++) - if (!*s2) return &((char*)string)[i]; +char *my_strstr(char *a, char *b) +{ + int l = lstrlen(b); + while (lstrlen(a) >= l) + { + char c = a[l]; + a[l] = 0; + if (!lstrcmpi(a, b)) + { + a[l] = c; + return a; + } + a[l] = c; + a = CharNext(a); } - return 0; + return NULL; } unsigned int my_atoi(char *s) { diff --git a/Plugins/InstallOptions.dll b/Plugins/InstallOptions.dll index d5f1575c81af12f4cb04cda59176d91f6b0f12f3..dd0de3bd2dbebe5f36c2e1ee4369a891ae534dd2 100644 GIT binary patch delta 6179 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 HzeV>q<@o^( delta 6138 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 uvi;Kbfo;+DXWL(GMRi;1n(I31dg{*DU$