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:
parent
a104daf046
commit
24d4c2678a
2 changed files with 224 additions and 181 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue