Fixed nsExec Unicode and tab output parsing (bug #1232)

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7121 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2019-09-11 23:36:32 +00:00
parent a104daf046
commit 24d4c2678a
2 changed files with 224 additions and 181 deletions

View file

@ -1,5 +1,6 @@
/* /*
Copyright (c) 2002 Robert Rainwater <rrainwater@yahoo.com> Copyright (C) 2002 Robert Rainwater <rrainwater@yahoo.com>
Copyright (C) 2002-2019 Nullsoft and Contributors
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -17,8 +18,6 @@ freely, subject to the following restrictions:
misrepresented as being the original software. misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
Unicode support by Jim Park -- 08/24/2007
*/ */
#include <windows.h> #include <windows.h>
#include <commctrl.h> #include <commctrl.h>
@ -32,16 +31,15 @@ FORCEINLINE DWORD NoDepr_GetVersion() { __pragma(warning(push))__pragma(warning(
#endif //~ _MSC_VER >= 1500 #endif //~ _MSC_VER >= 1500
#endif //~ _MSC_VER #endif //~ _MSC_VER
#ifndef true #define TAB_REPLACE _T(" ")
#define true TRUE #define TAB_REPLACE_SIZE (sizeof(TAB_REPLACE) - sizeof(_T("")))
#endif #define TAB_REPLACE_CCH (TAB_REPLACE_SIZE / sizeof(_T("")))
#ifndef false enum { MODE_IGNOREOUTPUT = 0, MODE_LINES = 1, MODE_STACK = 2 };
#define false FALSE
#endif
#define LOOPTIMEOUT 100 #define LOOPTIMEOUT 100
HWND g_hwndParent; HWND g_hwndParent;
HWND g_hwndList; HWND g_hwndList;
HINSTANCE g_hInst;
void ExecScript(BOOL log); void ExecScript(BOOL log);
void LogMessage(const TCHAR *pStr, BOOL bOEM); void LogMessage(const TCHAR *pStr, BOOL bOEM);
@ -50,37 +48,64 @@ unsigned int my_atoi(TCHAR *s);
int WINAPI AsExeWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow); int WINAPI AsExeWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
void __declspec(dllexport) Exec(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) { void __declspec(dllexport) Exec(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
g_hwndParent=hwndParent; g_hwndParent = hwndParent;
EXDLL_INIT(); EXDLL_INIT();
{ ExecScript(MODE_IGNOREOUTPUT);
ExecScript(0);
}
} }
void __declspec(dllexport) ExecToLog(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) { void __declspec(dllexport) ExecToLog(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
g_hwndParent=hwndParent; g_hwndParent = hwndParent;
EXDLL_INIT(); EXDLL_INIT();
{ ExecScript(MODE_LINES);
ExecScript(1);
}
} }
void __declspec(dllexport) ExecToStack(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) { void __declspec(dllexport) ExecToStack(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
g_hwndParent=hwndParent; g_hwndParent = hwndParent;
EXDLL_INIT(); EXDLL_INIT();
{ ExecScript(MODE_STACK);
ExecScript(2);
}
} }
HINSTANCE g_hInst;
BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) {
g_hInst = hInst; g_hInst = hInst;
return TRUE; return TRUE;
} }
#define TAB_REPLACE _T(" ") static BOOL IsLeadSurrogateUTF16(unsigned short c) {
#define TAB_REPLACE_SIZE (sizeof(TAB_REPLACE)-1) return c >= 0xd800 && c <= 0xdbff;
}
static void TruncateStringUTF16LE(LPWSTR Buffer, SIZE_T Length, LPCWSTR Overflow, SIZE_T lenOver) {
if (Length) {
LPWSTR p = &Buffer[Length - 1];
UINT stripBaseCharIfCuttingCombining = TRUE;
// CharNextW is buggy on XP&2003 but we don't care enough to call GetStringTypeW (http://archives.miloush.net/michkap/archive/2005/01/30/363420.html)
if (stripBaseCharIfCuttingCombining && lenOver) {
WCHAR buf[] = { *p, Overflow[0], lenOver > 1 ? Overflow[1] : L' ', L'\0' };
for (;;) {
BOOL comb = CharNextW(buf) > buf + 1;
if (!comb || p < Buffer) break;
*((WORD*)((BYTE*)&buf[1])) = *((WORD*)((BYTE*)&buf[0]));
buf[0] = *p;
*p-- = L'\0';
}
}
if (IsLeadSurrogateUTF16(*p)) {
*p = L'\0'; // Avoid incomplete pair
}
}
}
static void TruncateStringMB(UINT Codepage, LPSTR Buffer, SIZE_T Length, unsigned short OverflowCh) {
if (Length) {
CHAR *p = &Buffer[Length - 1], buf[] = { *p, ' ', ' ', '\0' };
if (CharNextExA(Codepage, buf, 0) > buf + 1) { // Remove incomplete DBCS character?
*p = '\0';
}
}
}
static BOOL IsWOW64() { static BOOL IsWOW64() {
#ifdef _WIN64 #ifdef _WIN64
@ -116,47 +141,25 @@ static BOOL IsWOW64() {
#endif #endif
} }
/**
* Convert the ansiStr if storing ANSI strings, otherwise, assume that the
* string is wide and don't convert, but straight copy.
* @param ansiStr [in] the suspected ANSI string.
* @param wideBuf [out] the buffer to write to.
* @param cnt [in] the size of widebuf in wchar_t's.
* @return true, if ASCII, false if suspected as wide.
*/
BOOL WideConvertIfASCII(const char* ansiStr, int strLen, WCHAR* wideBuf, int cnt, BOOL OEMCP)
{
BOOL rval = FALSE;
wideBuf[0] = 0;
if (lstrlenA(ansiStr) == strLen)
{
MultiByteToWideChar(OEMCP ? CP_OEMCP : CP_ACP, 0, ansiStr, -1, wideBuf, cnt);
rval = TRUE;
}
else
{
// Going to assume that it's a wide char array.
lstrcpynW(wideBuf, (const WCHAR*) ansiStr, cnt);
}
return rval; void ExecScript(int mode) {
} TCHAR szRet[128];
TCHAR meDLLPath[MAX_PATH];
void ExecScript(int log) { TCHAR *g_exec, *executor;
TCHAR szRet[128] = _T("");
TCHAR meDLLPath[MAX_PATH];
TCHAR *executor;
TCHAR *g_exec;
TCHAR *pExec; TCHAR *pExec;
unsigned int g_to; int ignoreData = mode == MODE_IGNOREOUTPUT;
int logMode = mode == MODE_LINES, stackMode = mode == MODE_STACK;
unsigned int to, tabExpandLength = logMode ? TAB_REPLACE_CCH : 0, codepage;
BOOL bOEM; BOOL bOEM;
*szRet = _T('\0');
if (!IsWOW64()) { if (!IsWOW64()) {
TCHAR* p; TCHAR* p;
int nComSpecSize; int nComSpecSize;
nComSpecSize = GetModuleFileName(g_hInst, meDLLPath, MAX_PATH) + 2; // 2 chars for quotes nComSpecSize = GetModuleFileName(g_hInst, meDLLPath, MAX_PATH) + 2; // 2 chars for quotes
g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR)*(g_stringsize+nComSpecSize+2)); // 1 for space, 1 for null g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR) * (g_stringsize+nComSpecSize+2)); // 1 for space, 1 for null
p = meDLLPath + nComSpecSize - 2; // point p at null char of meDLLPath p = meDLLPath + nComSpecSize - 2; // point p at null char of meDLLPath
*g_exec = _T('"'); *g_exec = _T('"');
executor = g_exec + 1; executor = g_exec + 1;
@ -215,18 +218,18 @@ void ExecScript(int log) {
pExec++; pExec++;
} else { } else {
executor = NULL; executor = NULL;
g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR)*(g_stringsize+1)); // 1 for NULL g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR) * (g_stringsize+1)); // 1 for NULL
pExec = g_exec; pExec = g_exec;
} }
g_to = 0; // default is no timeout to = 0; // default is no timeout
bOEM = FALSE; // default is no OEM->ANSI conversion bOEM = FALSE; // default is no OEM->ANSI conversion
g_hwndList = NULL; g_hwndList = NULL;
// g_hwndParent = the caller, usually NSIS installer. // g_hwndParent = the caller, usually NSIS installer.
if (g_hwndParent) // The window class name for dialog boxes is "#32770" if (g_hwndParent) // The window class name for dialog boxes is "#32770"
g_hwndList = FindWindowEx(FindWindowEx(g_hwndParent,NULL,_T("#32770"),NULL),NULL,_T("SysListView32"),NULL); g_hwndList = FindWindowEx(FindWindowEx(g_hwndParent, NULL, _T("#32770"), NULL), NULL, _T("SysListView32"), NULL);
// g_exec is the complete command to run: It has the copy of this DLL turned // g_exec is the complete command to run: It has the copy of this DLL turned
// into an executable right now. // into an executable right now.
@ -236,7 +239,7 @@ params:
popstring(pExec); popstring(pExec);
if (my_strstr(pExec, _T("/TIMEOUT=")) == pExec) { if (my_strstr(pExec, _T("/TIMEOUT=")) == pExec) {
TCHAR *szTimeout = pExec + 9; TCHAR *szTimeout = pExec + 9;
g_to = my_atoi(szTimeout); to = my_atoi(szTimeout);
*pExec = 0; *pExec = 0;
goto params; goto params;
} }
@ -259,155 +262,192 @@ params:
// Got all the params off the stack. // Got all the params off the stack.
{ {
STARTUPINFO si={sizeof(si),}; STARTUPINFO si = { sizeof(si), };
SECURITY_ATTRIBUTES sa={sizeof(sa),}; SECURITY_ATTRIBUTES sa = { sizeof(sa), };
SECURITY_DESCRIPTOR sd={0,}; SECURITY_DESCRIPTOR sd = { 0, };
PROCESS_INFORMATION pi={0,}; PROCESS_INFORMATION pi;
const BOOL isNT = sizeof(void*) > 4 || (GetVersion() < 0x80000000); const BOOL isNT = sizeof(void*) > 4 || (GetVersion() < 0x80000000);
HANDLE newstdout=0,read_stdout=0; HANDLE newstdout = 0, read_stdout = 0;
HANDLE newstdin=0,read_stdin=0; HANDLE newstdin = 0, read_stdin = 0;
DWORD dwRead = 1; int utfSource = sizeof(TCHAR) > 1 ? -1 : FALSE, utfOutput = sizeof(TCHAR) > 1;
DWORD dwExit = 0; DWORD cbRead, dwLastOutput;
DWORD dwWait = WAIT_TIMEOUT; DWORD dwExit = 0, waitResult = WAIT_TIMEOUT;
DWORD dwLastOutput; static BYTE bufSrc[1024];
static TCHAR szBuf[1024]; BYTE *pSrc;
#ifdef _UNICODE SIZE_T cbSrcTot = sizeof(bufSrc), cbSrc = 0, cbSrcFree;
static char ansiBuf[1024]; TCHAR *bufOutput = 0, *pNewAlloc, *pD;
#endif SIZE_T cchAlloc, cbAlloc, cchFree;
HGLOBAL hUnusedBuf = NULL; codepage = bOEM ? CP_OEMCP : CP_ACP;
TCHAR *szUnusedBuf = 0;
if (log) { if (!ignoreData) {
hUnusedBuf = GlobalAlloc(GHND, log & 2 ? (g_stringsize*sizeof(TCHAR)) : sizeof(szBuf)*4); // Note: will not grow if (log & 2) cbAlloc = stackMode ? (g_stringsize * sizeof(TCHAR)) : sizeof(bufSrc) * 4, cchAlloc = cbAlloc / sizeof(TCHAR);
if (!hUnusedBuf) { pD = bufOutput = GlobalAlloc(GPTR, cbAlloc + sizeof(TCHAR)); // Include "hidden" space for a \0
if (!bufOutput) {
lstrcpy(szRet, _T("error")); lstrcpy(szRet, _T("error"));
goto done; goto done;
} }
szUnusedBuf = (TCHAR *)GlobalLock(hUnusedBuf); *bufOutput = _T('\0');
} }
sa.bInheritHandle = true; sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL; sa.lpSecurityDescriptor = NULL;
if (isNT) { if (isNT) {
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION); InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd,true,NULL,false); SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
sa.lpSecurityDescriptor = &sd; sa.lpSecurityDescriptor = &sd;
} }
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) { if (!CreatePipe(&read_stdout, &newstdout, &sa, 0)) {
lstrcpy(szRet, _T("error")); lstrcpy(szRet, _T("error"));
goto done; goto done;
} }
if (!CreatePipe(&read_stdin,&newstdin,&sa,0)) { if (!CreatePipe(&read_stdin, &newstdin, &sa, 0)) {
lstrcpy(szRet, _T("error")); lstrcpy(szRet, _T("error"));
goto done; goto done;
} }
GetStartupInfo(&si); GetStartupInfo(&si); // Why?
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; si.wShowWindow = SW_HIDE;
si.hStdInput = newstdin; si.hStdInput = newstdin;
si.hStdOutput = newstdout; si.hStdOutput = newstdout;
si.hStdError = newstdout; si.hStdError = newstdout;
if (!CreateProcess(NULL,g_exec,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { if (!CreateProcess(NULL, g_exec, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
pi.hProcess = pi.hThread = NULL;
lstrcpy(szRet, _T("error")); lstrcpy(szRet, _T("error"));
goto done; goto done;
} }
dwLastOutput = GetTickCount();
// Now I'm talking with an executable copy of myself. // Now I'm talking with an executable copy of myself.
while (dwWait != WAIT_OBJECT_0 || dwRead) { dwLastOutput = GetTickCount();
PeekNamedPipe(read_stdout, 0, 0, 0, &dwRead, NULL); for (;;) {
if (dwRead) { TCHAR bufCh[2];
dwLastOutput = GetTickCount(); waitForProcess:
#ifdef _UNICODE waitResult = WaitForSingleObject(pi.hProcess, 0);
ReadFile(read_stdout, ansiBuf, sizeof(ansiBuf)-1, &dwRead, NULL); GetExitCodeProcess(pi.hProcess, &dwExit);
ansiBuf[dwRead] = 0; readMore:
WideConvertIfASCII(ansiBuf, dwRead, szBuf, sizeof(szBuf)/sizeof(szBuf[0]), bOEM); PeekNamedPipe(read_stdout, 0, 0, 0, &cbRead, NULL);
#else if (!cbRead) {
ReadFile(read_stdout, szBuf, sizeof(szBuf)-1, &dwRead, NULL); if (waitResult == WAIT_OBJECT_0) {
szBuf[dwRead] = '\0'; break; // No data in the pipe and process ended, we are done
#endif
if (log) {
if (log & 2) {
lstrcpyn(szUnusedBuf + lstrlen(szUnusedBuf), szBuf, g_stringsize - lstrlen(szUnusedBuf));
}
else {
TCHAR *p, *p2;
SIZE_T iReqLen = lstrlen(szBuf) + lstrlen(szUnusedBuf) + 1;
if (GlobalSize(hUnusedBuf) < iReqLen*sizeof(TCHAR)) {
GlobalUnlock(hUnusedBuf);
hUnusedBuf = GlobalReAlloc(hUnusedBuf, iReqLen*sizeof(TCHAR)+sizeof(szBuf), GHND);
if (!hUnusedBuf) {
lstrcpy(szRet, _T("error"));
break;
}
szUnusedBuf = (TCHAR *)GlobalLock(hUnusedBuf);
}
p = szUnusedBuf; // get the old left overs
lstrcat(p, szBuf);
while ((p = my_strstr(p, _T("\t")))) {
if ((int)(p - szUnusedBuf) > (int)(GlobalSize(hUnusedBuf)/sizeof(TCHAR) - TAB_REPLACE_SIZE - 1))
{
*p++ = _T(' ');
}
else
{
int len = lstrlen(p);
TCHAR *c_out=(TCHAR*)p+TAB_REPLACE_SIZE+len;
TCHAR *c_in=(TCHAR *)p+len;
while (len-- > 0) {
*c_out--=*c_in--;
}
lstrcpy(p, TAB_REPLACE);
p += TAB_REPLACE_SIZE;
*p = _T(' ');
}
}
p = szUnusedBuf; // get the old left overs
for (p2 = p; *p2;) {
if (*p2 == _T('\r')) {
*p2++ = 0;
continue;
}
if (*p2 == _T('\n')) {
*p2 = 0;
while (!*p && p != p2) p++;
LogMessage(p, bOEM);
p = ++p2;
continue;
}
p2 = CharNext(p2);
}
// If data was taken out from the unused buffer, move p contents to the start of szUnusedBuf
if (p != szUnusedBuf) {
TCHAR *p2 = szUnusedBuf;
while (*p) *p2++ = *p++;
*p2 = 0;
}
}
} }
} if (to && GetTickCount() > dwLastOutput+to) {
else {
if (g_to && GetTickCount() > dwLastOutput+g_to) {
TerminateProcess(pi.hProcess, -1); TerminateProcess(pi.hProcess, -1);
lstrcpy(szRet, _T("timeout")); lstrcpy(szRet, _T("timeout"));
} }
else Sleep(LOOPTIMEOUT); else {
Sleep(LOOPTIMEOUT);
}
continue;
} }
dwWait = WaitForSingleObject(pi.hProcess, 0); dwLastOutput = GetTickCount();
GetExitCodeProcess(pi.hProcess, &dwExit); ReadFile(read_stdout, bufSrc + cbSrc, cbSrcFree = cbSrcTot - cbSrc, &cbRead, NULL);
PeekNamedPipe(read_stdout, 0, 0, 0, &dwRead, NULL); cbSrcFree -= cbRead, cbSrc = cbSrcTot - cbSrcFree;
pSrc = bufSrc;
if (utfSource < 0 && cbSrc) { // Simple UTF-16LE detection
#ifdef UNICODE
utfSource = IsTextUnicode(pSrc, cbSrc & ~1, NULL) != FALSE;
#else
utfSource = (cbSrc >= 3 && pSrc[0] && !pSrc[1]) || (cbSrc > 4 && pSrc[2] && !pSrc[3]); // Lame latin-only test
utfSource |= (cbSrc > 3 && pSrc[0] == 0xFF && pSrc[1] == 0xFE && (pSrc[2] | pSrc[3])); // Lame BOM test
#endif
}
if (ignoreData) {
cbSrc = 0; // Overwrite the whole buffer every read
continue;
}
if (!cbRead) {
continue; // No new data, read more before trying to parse
}
parseLines:
cchFree = cchAlloc - (pD - bufOutput);
for (;;) {
DWORD cbSrcChar = 1, cchDstChar, i;
*pD = _T('\0'); // Terminate output buffer because we can unexpectedly run out of data
if (!cbSrc) {
goto readMore;
}
if (utfSource) { // UTF-16LE --> ?:
if (cbSrc < 2) {
goto readMore;
}
if (utfOutput) { // UTF-16LE --> UTF-16LE:
bufCh[0] = ((TCHAR*)pSrc)[0], cbSrcChar = sizeof(WCHAR), cchDstChar = 1; // We only care about certain ASCII characters so we don't bother dealing with surrogate pairs.
}
else { // UTF-16LE --> DBCS
// TODO: This is tricky because we need the complete base character (or surrogate pair) and all the trailing combining characters for a grapheme in the buffer before we can call WideCharToMultiByte.
utfOutput = FALSE; // For now we just treat it as DBCS
continue;
}
}
else { // DBCS --> ?:
if (utfOutput) { // DBCS --> UTF-16LE:
BOOL isMb = IsDBCSLeadByteEx(codepage,((CHAR*)pSrc)[0]);
if (isMb && cbSrc < ++cbSrcChar) {
goto readMore;
}
cchDstChar = MultiByteToWideChar(codepage,0,pSrc,cbSrcChar,(WCHAR*) bufCh,2);
}
else { // DBCS --> DBCS:
bufCh[0] = ((CHAR*)pSrc)[0], cchDstChar = 1; // Note: OEM codepage will be converted by LogMessage
}
}
if (bufCh[0] == _T('\t') && tabExpandLength) { // Expand tab to spaces?
if (cchFree < tabExpandLength) {
goto resizeOutputBuffer;
}
lstrcpy(pD, TAB_REPLACE);
pD += tabExpandLength, cchFree -= tabExpandLength;
}
else if (bufCh[0] == _T('\r') && logMode) {
// Eating it
}
else if (bufCh[0] == _T('\n') && logMode) {
LogMessage(bufOutput, bOEM); // Output has already been \0 terminated
*(pD = bufOutput) = _T('\0'), cchFree = cchAlloc;
}
else {
if (cchFree < cchDstChar) {
SIZE_T cchOrgOffset;
resizeOutputBuffer:
if (stackMode) {
ignoreData = TRUE; // Buffer was already maximum for the NSIS stack, we cannot handle more data
if (utfOutput)
TruncateStringUTF16LE((LPWSTR) bufOutput, pD - bufOutput, (LPCWSTR) bufCh, cchDstChar);
else
TruncateStringMB(codepage, (LPSTR) bufOutput, pD - bufOutput, bufCh[0]);
goto waitForProcess;
}
cchAlloc += 1024, cbAlloc = cchAlloc / sizeof(TCHAR);
pNewAlloc = GlobalReAlloc(bufOutput,cbAlloc + sizeof(TCHAR),GPTR|GMEM_MOVEABLE); // Include "hidden" space for a \0
if (!pNewAlloc) {
lstrcpy(szRet, _T("error"));
ignoreData = TRUE;
goto waitForProcess;
}
cchOrgOffset = pD - bufOutput;
*(pD = (bufOutput = pNewAlloc) + cchOrgOffset) = _T('\0');
goto parseLines;
}
for (i = 0; i < cchDstChar; ++i) {
*pD++ = bufCh[i], --cchFree;
}
}
pSrc += cbSrcChar, cbSrc -= cbSrcChar;
}
} }
done: done:
if (log & 2) pushstring(szUnusedBuf); if (stackMode) pushstring(bufOutput);
if (log & 1 && *szUnusedBuf) LogMessage(szUnusedBuf, bOEM); if (logMode && *bufOutput) LogMessage(bufOutput,bOEM); // Write remaining output
if ( dwExit == STATUS_ILLEGAL_INSTRUCTION ) if (dwExit == STATUS_ILLEGAL_INSTRUCTION)
lstrcpy(szRet, _T("error")); lstrcpy(szRet, _T("error"));
if (!szRet[0]) wsprintf(szRet,_T("%d"),dwExit); if (!szRet[0]) wsprintf(szRet,_T("%d"),dwExit);
pushstring(szRet); pushstring(szRet);
@ -419,12 +459,11 @@ done:
CloseHandle(read_stdin); CloseHandle(read_stdin);
if (pExec-2 >= g_exec) if (pExec-2 >= g_exec)
*(pExec-2) = _T('\0'); // skip space and quote *(pExec-2) = _T('\0'); // skip space and quote
if (executor) DeleteFile(executor); if (executor)
DeleteFile(executor);
GlobalFree(g_exec); GlobalFree(g_exec);
if (log) { if (bufOutput)
GlobalUnlock(hUnusedBuf); GlobalFree(bufOutput);
GlobalFree(hUnusedBuf);
}
} }
} }

View file

@ -10,17 +10,21 @@ ANSI targets are deprecated, consider moving to Unicode.
\S1{v3.05-cl} Changelog \S1{v3.05-cl} Changelog
\S2{} Minor Changes \S2{} Major Changes
\b Added \R{apeaddresource}{PEAddResource} and \R{aperemoveresource}{PERemoveResource} \b Added \R{apeaddresource}{PEAddResource} and \R{aperemoveresource}{PERemoveResource}
\b Added \R{loadandsetimage}{LoadAndSetImage} \b Added \R{loadandsetimage}{LoadAndSetImage}
\# Undocumented: \b Added ManifestMaxVersionTested \b Allow quoted library path in System::Call (\W{http://sf.net/p/nsis/bugs/546}{bug #546} and \W{http://sf.net/p/nsis/bugs/1225}{bug #1225})
\b Improved nsExec Unicode and tab output parsing (\W{http://sf.net/p/nsis/bugs/1232}{bug #1232})
\S2{} Minor Changes
\b Added experimental \R{amanifestlongpathaware}{ManifestLongPathAware} attribute \b Added experimental \R{amanifestlongpathaware}{ManifestLongPathAware} attribute
\b Allow quoted library path in System::Call (\W{http://sf.net/p/nsis/bugs/546}{bug #546} and \W{http://sf.net/p/nsis/bugs/1225}{bug #1225}) \# Undocumented: \b Added ManifestMaxVersionTested
\b %1 in !finalize command can be specified multiple times \b %1 in !finalize command can be specified multiple times