NSIS/Contrib/nsExec/nsexec.c
wizou 752d7d239a Jim Park's Unicode NSIS merging - Step 1 : switch to TCHARs where relevant.
Compiler output is identical before & after this step

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/branches/wizou@6036 212acab6-be3b-0410-9dea-997c60f758d6
2010-03-24 17:22:56 +00:00

494 lines
15 KiB
C

/*
Copyright (c) 2002 Robert Rainwater <rrainwater@yahoo.com>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
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 <commctrl.h>
#include <winnt.h>
#include <nsis/pluginapi.h> // nsis plugin
#ifndef true
#define true TRUE
#endif
#ifndef false
#define false FALSE
#endif
#define LOOPTIMEOUT 100
HWND g_hwndParent;
HWND g_hwndList;
void ExecScript(BOOL log);
void LogMessage(const TCHAR *pStr, BOOL bOEM);
TCHAR *my_strstr(TCHAR *a, TCHAR *b);
unsigned int my_atoi(TCHAR *s);
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
void __declspec(dllexport) Exec(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
g_hwndParent=hwndParent;
EXDLL_INIT();
{
ExecScript(0);
}
}
void __declspec(dllexport) ExecToLog(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
g_hwndParent=hwndParent;
EXDLL_INIT();
{
ExecScript(1);
}
}
void __declspec(dllexport) ExecToStack(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
g_hwndParent=hwndParent;
EXDLL_INIT();
{
ExecScript(2);
}
}
HINSTANCE g_hInst;
BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) {
g_hInst = hInst;
return TRUE;
}
#define TAB_REPLACE _T(" ")
#define TAB_REPLACE_SIZE (sizeof(TAB_REPLACE)-1)
// 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
BOOL IsWOW64() {
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
BOOL wow64;
LPFN_ISWOW64PROCESS fnIsWow64Process;
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(_T("kernel32")), "IsWow64Process");
if (fnIsWow64Process != NULL) {
if (fnIsWow64Process(GetCurrentProcess(), &wow64)) {
return wow64;
}
}
return FALSE;
}
void ExecScript(int log) {
TCHAR szRet[128] = _T("");
TCHAR meDLLPath[MAX_PATH];
TCHAR *executor;
TCHAR *g_exec;
TCHAR *pExec;
unsigned int g_to;
BOOL bOEM;
if (!IsWOW64()) {
TCHAR* p;
int nComSpecSize;
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
p = meDLLPath + nComSpecSize - 2; // point p at null char of meDLLPath
*g_exec = _T('"');
executor = g_exec + 1;
// Look for the last '\' in path.
do
{
if (*p == _T('\\'))
break;
p = CharPrev(meDLLPath, p);
}
while (p > meDLLPath);
if (p == meDLLPath)
{
// bad path
pushstring(_T("error"));
GlobalFree(g_exec);
return;
}
*p = 0;
GetTempFileName(meDLLPath, _T("ns"), 0, executor); // executor = new temp file name in module path.
*p = _T('\\');
if (CopyFile(meDLLPath, executor, FALSE)) // copy current DLL to temp file in module path.
{
HANDLE hFile, hMapping;
LPBYTE pMapView;
PIMAGE_NT_HEADERS pNTHeaders;
hFile = CreateFile(executor, 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)
{
pNTHeaders = (PIMAGE_NT_HEADERS)(pMapView + ((PIMAGE_DOS_HEADER)pMapView)->e_lfanew);
// Turning the copied DLL into a stripped down executable.
pNTHeaders->FileHeader.Characteristics = IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_LOCAL_SYMS_STRIPPED |
IMAGE_FILE_LINE_NUMS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE;
// Windows character-mode user interface (CUI) subsystem.
pNTHeaders->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
pNTHeaders->OptionalHeader.AddressOfEntryPoint = (DWORD)_tWinMain - (DWORD)g_hInst;
UnmapViewOfFile(pMapView);
}
CloseHandle(hMapping);
CloseHandle(hFile);
}
lstrcat(g_exec, _T("\""));
// add space
pExec = g_exec + lstrlen(g_exec);
*pExec = _T(' ');
pExec++;
} else {
executor = NULL;
g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR)*(g_stringsize+1)); // 1 for NULL
pExec = g_exec;
}
g_to = 0; // default is no timeout
bOEM = FALSE; // default is no OEM->ANSI conversion
g_hwndList = NULL;
// g_hwndParent = the caller, usually NSIS installer.
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_exec is the complete command to run: It has the copy of this DLL turned
// into an executable right now.
params:
// Get the command I need to run from the NSIS stack.
popstring(pExec);
if (my_strstr(pExec, _T("/TIMEOUT=")) == pExec) {
TCHAR *szTimeout = pExec + 9;
g_to = my_atoi(szTimeout);
*pExec = 0;
goto params;
}
if (!lstrcmpi(pExec, _T("/OEM"))) {
bOEM = TRUE;
*pExec = 0;
goto params;
}
if (!pExec[0])
{
pushstring(_T("error"));
*(pExec-2) = _T('\0'); // skip space and quote
if (executor) DeleteFile(executor);
GlobalFree(g_exec);
return;
}
// Got all the params off the stack.
{
STARTUPINFO si={sizeof(si),};
SECURITY_ATTRIBUTES sa={sizeof(sa),};
SECURITY_DESCRIPTOR sd={0,};
PROCESS_INFORMATION pi={0,};
OSVERSIONINFO osv={sizeof(osv)};
HANDLE newstdout=0,read_stdout=0;
HANDLE newstdin=0,read_stdin=0;
DWORD dwRead = 1;
DWORD dwExit = 0;
DWORD dwWait = WAIT_TIMEOUT;
DWORD dwLastOutput;
static TCHAR szBuf[1024];
HGLOBAL hUnusedBuf = NULL;
TCHAR *szUnusedBuf = 0;
if (log) {
hUnusedBuf = GlobalAlloc(GHND, log & 2 ? (g_stringsize*sizeof(TCHAR)) : sizeof(szBuf)*4);
if (!hUnusedBuf) {
lstrcpy(szRet, _T("error"));
goto done;
}
szUnusedBuf = (TCHAR *)GlobalLock(hUnusedBuf);
}
GetVersionEx(&osv); // Get OS info
if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT) {
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd,true,NULL,false);
sa.lpSecurityDescriptor = &sd;
}
else
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true;
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) {
lstrcpy(szRet, _T("error"));
goto done;
}
if (!CreatePipe(&read_stdin,&newstdin,&sa,0)) {
lstrcpy(szRet, _T("error"));
goto done;
}
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdInput = newstdin;
si.hStdOutput = newstdout;
si.hStdError = newstdout;
if (!CreateProcess(NULL,g_exec,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) {
lstrcpy(szRet, _T("error"));
goto done;
}
dwLastOutput = GetTickCount();
// Now I'm talking with an executable copy of myself.
while (dwWait != WAIT_OBJECT_0 || dwRead) {
PeekNamedPipe(read_stdout, 0, 0, 0, &dwRead, NULL);
if (dwRead) {
dwLastOutput = GetTickCount();
ReadFile(read_stdout, szBuf, sizeof(szBuf)-1, &dwRead, NULL);
szBuf[dwRead] = 0;
if (log) {
TCHAR *p, *p2;
SIZE_T iReqLen = lstrlen(szBuf) + lstrlen(szUnusedBuf);
if (GlobalSize(hUnusedBuf) < iReqLen && (iReqLen < g_stringsize || !(log & 2))) {
GlobalUnlock(hUnusedBuf);
hUnusedBuf = GlobalReAlloc(hUnusedBuf, iReqLen+sizeof(szBuf), GHND);
if (!hUnusedBuf) {
lstrcpy(szRet, _T("error"));
break;
}
szUnusedBuf = (TCHAR *)GlobalLock(hUnusedBuf);
}
p = szUnusedBuf; // get the old left overs
if (iReqLen < g_stringsize || !(log & 2)) lstrcat(p, szBuf);
else {
lstrcpyn(p + lstrlen(p), szBuf, g_stringsize - lstrlen(p));
}
if (!(log & 2)) {
while ((p = my_strstr(p, _T("\t")))) {
if ((int)(p - szUnusedBuf) > (int)(GlobalSize(hUnusedBuf) - 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;
}
}
}
}
else {
if (g_to && GetTickCount() > dwLastOutput+g_to) {
TerminateProcess(pi.hProcess, -1);
lstrcpy(szRet, _T("timeout"));
}
else Sleep(LOOPTIMEOUT);
}
dwWait = WaitForSingleObject(pi.hProcess, 0);
GetExitCodeProcess(pi.hProcess, &dwExit);
PeekNamedPipe(read_stdout, 0, 0, 0, &dwRead, NULL);
}
done:
if (log & 2) pushstring(szUnusedBuf);
if (log & 1 && *szUnusedBuf) LogMessage(szUnusedBuf, bOEM);
if ( dwExit == STATUS_ILLEGAL_INSTRUCTION )
lstrcpy(szRet, _T("error"));
if (!szRet[0]) wsprintf(szRet,_T("%d"),dwExit);
pushstring(szRet);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(newstdin);
CloseHandle(read_stdin);
*(pExec-2) = _T('\0'); // skip space and quote
if (executor) DeleteFile(executor);
GlobalFree(g_exec);
if (log) {
GlobalUnlock(hUnusedBuf);
GlobalFree(hUnusedBuf);
}
}
}
// Tim Kosse's LogMessage
void LogMessage(const TCHAR *pStr, BOOL bOEM) {
LVITEM item={0};
int nItemCount;
if (!g_hwndList) return;
//if (!lstrlen(pStr)) return;
if (bOEM == TRUE) OemToCharBuff(pStr, (TCHAR *)pStr, lstrlen(pStr));
nItemCount=SendMessage(g_hwndList, LVM_GETITEMCOUNT, 0, 0);
item.mask=LVIF_TEXT;
item.pszText=(TCHAR *)pStr;
item.cchTextMax=0;
item.iItem=nItemCount;
ListView_InsertItem(g_hwndList, &item);
ListView_EnsureVisible(g_hwndList, item.iItem, 0);
}
TCHAR *my_strstr(TCHAR *a, TCHAR *b)
{
int l = lstrlen(b);
while (lstrlen(a) >= l)
{
TCHAR c = a[l];
a[l] = 0;
if (!lstrcmpi(a, b))
{
a[l] = c;
return a;
}
a[l] = c;
a = CharNext(a);
}
return NULL;
}
unsigned int my_atoi(TCHAR *s) {
unsigned int v=0;
if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) {
s+=2;
for (;;) {
int c=*s++;
if (c >= _T('0') && c <= _T('9')) c-=_T('0');
else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10;
else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10;
else break;
v<<=4;
v+=c;
}
}
else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) {
s++;
for (;;) {
int c=*s++;
if (c >= _T('0') && c <= _T('7')) c-=_T('0');
else break;
v<<=3;
v+=c;
}
}
else {
for (;;) {
int c=*s++ - _T('0');
if (c < 0 || c > 9) break;
v*=10;
v+=c;
}
}
return (int)v;
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
DWORD Ret;
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
TCHAR command_line[1024];
TCHAR seekchar=_T(' ');
TCHAR *cmdline;
si.cb = sizeof(si);
// Make child process use this app's standard files. Not needed because the handles
// we created when executing this process were inheritable.
//si.dwFlags = STARTF_USESTDHANDLES;
//si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
//si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
//si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
lstrcpyn(command_line, GetCommandLine(), 1024);
cmdline = command_line;
if (*cmdline == _T('\"')) seekchar = *cmdline++;
while (*cmdline && *cmdline != seekchar) cmdline=CharNext(cmdline);
cmdline=CharNext(cmdline);
// skip any spaces before the arguments
while (*cmdline && *cmdline == _T(' ')) cmdline++;
Ret = CreateProcess (NULL, cmdline,
NULL, NULL,
TRUE, 0,
NULL, NULL,
&si, &pi
);
if (Ret)
{
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &Ret);
CloseHandle (pi.hProcess);
CloseHandle (pi.hThread);
ExitProcess(Ret);
}
else
{
ExitProcess(STATUS_ILLEGAL_INSTRUCTION);
}
return 0; // dummy
}