StdOut Unicode support, controlled by /OUTPUTCHARSET and/or existing BOM if redirected.

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6350 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2013-05-04 13:32:47 +00:00
parent c6f149764d
commit 0653f46c07
19 changed files with 684 additions and 425 deletions

View file

@ -27,12 +27,6 @@ int GetTLBVersion(tstring& filepath, DWORD& high, DWORD & low)
{
#ifdef _WIN32
#ifdef _countof
#define COUNTOF _countof
#else
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#endif
int found = 0;
TCHAR fullpath[1024];

View file

@ -413,6 +413,14 @@ BOOL CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
DragAcceptFiles(g_sdata.hwnd,TRUE);
return TRUE;
}
case MakensisAPI::QUERYHOST: {
if (MakensisAPI::QH_OUTPUTCHARSET) {
const UINT reqcp = CP_UTF8; // TODO: UTF16LE will be faster for makensis
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)(1+reqcp));
return TRUE;
}
return FALSE;
}
case WM_NOTIFY:
switch (((NMHDR*)lParam)->code ) {
case EN_SELCHANGE:
@ -712,12 +720,12 @@ BOOL CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
DWORD WINAPI MakeNSISProc(LPVOID p) {
TCHAR eventnamebuf[100];
wsprintf(eventnamebuf,MakensisAPI::SigintEventNameFmt,g_sdata.hwnd);
wsprintf(eventnamebuf, MakensisAPI::SigintEventNameFmt, g_sdata.hwnd);
if (g_sdata.sigint_event) CloseHandle(g_sdata.sigint_event);
g_sdata.sigint_event = CreateEvent(NULL,FALSE,FALSE,eventnamebuf);
g_sdata.sigint_event = CreateEvent(NULL, FALSE, FALSE, eventnamebuf);
if (!g_sdata.sigint_event) {
ErrorMessage(g_sdata.hwnd,_T("There was an error creating the abort event."));
PostMessage(g_sdata.hwnd,WM_MAKENSIS_PROCESSCOMPLETE,0,0);
ErrorMessage(g_sdata.hwnd, _T("There was an error creating the abort event."));
PostMessage(g_sdata.hwnd, WM_MAKENSIS_PROCESSCOMPLETE, 0, 0);
return 1;
}
@ -725,44 +733,21 @@ DWORD WINAPI MakeNSISProc(LPVOID p) {
TCHAR buf[1024];
#endif
char iobuf[1024]; //i/o buffer
STARTUPINFO si={sizeof(si),};
SECURITY_ATTRIBUTES sa={sizeof(sa),};
SECURITY_DESCRIPTOR sd={0,};
PROCESS_INFORMATION pi={0,};
HANDLE newstdout=0,read_stdout=0;
HANDLE newstdin=0,read_stdin=0;
OSVERSIONINFO osv={sizeof(osv)};
GetVersionEx(&osv);
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)) {
ErrorMessage(g_sdata.hwnd,_T("There was an error creating the output pipe."));
PostMessage(g_sdata.hwnd,WM_MAKENSIS_PROCESSCOMPLETE,0,0);
STARTUPINFO si;
HANDLE newstdout,read_stdout;
if (!InitSpawn(si, read_stdout, newstdout)) {
ErrorMessage(g_sdata.hwnd, _T("There was an error creating the pipe."));
PostMessage(g_sdata.hwnd, WM_MAKENSIS_PROCESSCOMPLETE, 0, 0);
return 1;
}
if (!CreatePipe(&read_stdin,&newstdin,&sa,0)) {
ErrorMessage(g_sdata.hwnd,_T("There was an error creating the input pipe."));
PostMessage(g_sdata.hwnd,WM_MAKENSIS_PROCESSCOMPLETE,0,0);
return 1;
}
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = newstdout;
si.hStdError = newstdout;
si.hStdInput = newstdin;
if (!CreateProcess(NULL,g_sdata.compile_command,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) {
TCHAR buf[MAX_STRING];
wsprintf(buf,_T("Could not execute:\r\n %s."),g_sdata.compile_command);
ErrorMessage(g_sdata.hwnd,buf);
CloseHandle(newstdout);
CloseHandle(read_stdout);
PostMessage(g_sdata.hwnd,WM_MAKENSIS_PROCESSCOMPLETE,0,0);
PROCESS_INFORMATION pi;
if (!CreateProcess(0, g_sdata.compile_command, 0, 0, TRUE, CREATE_NEW_CONSOLE, 0, 0, &si, &pi)) {
TCHAR buf[MAX_STRING]; // BUGBUG: TODO: Too small?
wsprintf(buf,_T("Could not execute:\r\n %s."), g_sdata.compile_command);
ErrorMessage(g_sdata.hwnd, buf);
FreeSpawn(0, read_stdout, newstdout);
PostMessage(g_sdata.hwnd, WM_MAKENSIS_PROCESSCOMPLETE, 0, 0);
return 1;
}
CloseHandle(newstdout); // close this handle (duplicated in subprocess) now so we get ERROR_BROKEN_PIPE
@ -774,8 +759,9 @@ DWORD WINAPI MakeNSISProc(LPVOID p) {
#ifdef _UNICODE
// this tweak is to prevent LogMessage from cutting in the middle of an UTF-8 sequence
// we print only up to the latest \n of the buffer, and keep the remaining for the next loop
// BUGBUG: What if the line is longer than sizeof(iobuf)?
char* lastLF = strrchr(iobuf,'\n');
if (lastLF == NULL) lastLF = iobuf+dwRead-1;
if (!lastLF) lastLF = iobuf+dwRead-1;
char ch = *++lastLF;
*lastLF = '\0';
MultiByteToWideChar(CP_UTF8,0,iobuf,lastLF+1-iobuf,buf,COUNTOF(buf));
@ -794,15 +780,9 @@ DWORD WINAPI MakeNSISProc(LPVOID p) {
MultiByteToWideChar(CP_UTF8,0,iobuf,dwRead+1,buf,COUNTOF(buf));
LogMessage(g_sdata.hwnd, buf);
#endif
DWORD dwExit;
GetExitCodeProcess(pi.hProcess, &dwExit);
g_sdata.retcode = dwExit;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(read_stdout);
CloseHandle(newstdin);
CloseHandle(read_stdin);
PostMessage(g_sdata.hwnd,WM_MAKENSIS_PROCESSCOMPLETE,0,0);
FreeSpawn(&pi, read_stdout, 0);
g_sdata.retcode = pi.dwProcessId;
PostMessage(g_sdata.hwnd, WM_MAKENSIS_PROCESSCOMPLETE, 0, 0);
return 0;
}

View file

@ -85,6 +85,12 @@ namespace MakensisAPI {
NOTIFY_ERROR,
NOTIFY_OUTPUT
};
enum sndmsg_e {
QUERYHOST = WM_APP
};
enum QUERYHOST_e {
QH_OUTPUTCHARSET = 1
};
}
typedef enum {

View file

@ -568,57 +568,73 @@ void ResetSymbols() {
}
}
int InitBranding() {
TCHAR *s;
TCHAR opt[] = _T(" /version");
s = (TCHAR *)GlobalAlloc(GPTR,(lstrlen(EXENAME)+lstrlen(opt)+1)*sizeof(TCHAR));
lstrcpy(s, EXENAME);
lstrcat(s, opt);
{
STARTUPINFO si={sizeof(si),};
SECURITY_ATTRIBUTES sa={sizeof(sa),};
SECURITY_DESCRIPTOR sd={0,};
PROCESS_INFORMATION pi={0,};
HANDLE newstdout=0,read_stdout=0;
void FreeSpawn(PROCESS_INFORMATION *pPI, HANDLE hRd, HANDLE hWr) {
if (pPI) {
GetExitCodeProcess(pPI->hProcess, &pPI->dwProcessId);
CloseHandle(pPI->hProcess);
CloseHandle(pPI->hThread);
}
CloseHandle(hRd);
CloseHandle(hWr);
}
BOOL InitSpawn(STARTUPINFO &si, HANDLE &hRd, HANDLE &hWr) {
OSVERSIONINFO osv = {sizeof(osv)};
GetVersionEx(&osv);
const bool winnt = VER_PLATFORM_WIN32_NT == osv.dwPlatformId;
OSVERSIONINFO osv={sizeof(osv)};
GetVersionEx(&osv);
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)) {
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
SECURITY_ATTRIBUTES sa={sizeof(sa)};
SECURITY_DESCRIPTOR sd;
if (winnt) {
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, true, NULL, false);
sa.lpSecurityDescriptor = &sd;
}
sa.bInheritHandle = true;
BOOL okp = CreatePipe(&hRd, &hWr, &sa, 0);
si.hStdOutput = hWr, si.hStdError = hWr;
si.hStdInput = INVALID_HANDLE_VALUE;
return okp;
}
int InitBranding() {
const TCHAR *opt = _T(" /version");
UINT cch = lstrlen(EXENAME) + lstrlen(opt) + 1;
TCHAR *s = (TCHAR *)GlobalAlloc(GPTR, cch*sizeof(TCHAR));
if (s) {
lstrcpy(s, EXENAME);
lstrcat(s, opt);
STARTUPINFO si;
HANDLE newstdout, read_stdout;
if (!InitSpawn(si, read_stdout, newstdout)) return 0;
PROCESS_INFORMATION pi;
if (!CreateProcess(0, s, 0, 0, TRUE, CREATE_NEW_CONSOLE, 0, 0, &si, &pi)) {
FreeSpawn(0, read_stdout, newstdout);
return 0;
}
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = newstdout;
si.hStdError = newstdout;
if (!CreateProcess(NULL,s,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) {
CloseHandle(newstdout);
CloseHandle(read_stdout);
return 0;
char szBuf[1024], retval = 0;
DWORD dwRead = 0;
if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, 10000)) {
ReadFile(read_stdout, szBuf, sizeof(szBuf)-1, &dwRead, NULL);
retval = 1;
}
char szBuf[1024];
DWORD dwRead = 1;
if (WaitForSingleObject(pi.hProcess,10000)!=WAIT_OBJECT_0) {
return 0;
}
ReadFile(read_stdout, szBuf, sizeof(szBuf)-1, &dwRead, NULL);
FreeSpawn(&pi, read_stdout, newstdout);
szBuf[dwRead] = 0;
int len = lstrlenA(szBuf);
if (len==0) return 0;
g_sdata.branding = (TCHAR *)GlobalAlloc(GPTR,(len+6)*sizeof(TCHAR));
wsprintf(g_sdata.branding,_T("NSIS %hs"),szBuf);
g_sdata.brandingv = (char *)GlobalAlloc(GPTR,len+1);
lstrcpyA(g_sdata.brandingv,szBuf);
if (len==0) retval = 0;
g_sdata.branding = (TCHAR *)GlobalAlloc(GPTR, (len+6)*sizeof(TCHAR)); // LEAKED
wsprintf(g_sdata.branding, _T("NSIS %hs"), szBuf);
g_sdata.brandingv = (char *)GlobalAlloc(GPTR, len+1); // LEAKED
lstrcpyA(g_sdata.brandingv, szBuf);
GlobalFree(s);
return retval;
}
return 1;
return 0;
}
@ -627,7 +643,7 @@ void InitTooltips(HWND h) {
memset(&g_tip,0,sizeof(NTOOLTIP));
g_tip.tip_p = h;
INITCOMMONCONTROLSEX icx;
icx.dwSize = sizeof(icx);
icx.dwSize = sizeof(icx);
icx.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&icx);
DWORD dwStyle = WS_POPUP | WS_BORDER | TTS_ALWAYSTIP;

View file

@ -30,6 +30,9 @@
#define MRU_LIST_SIZE 5
#define MRU_DISPLAY_LENGTH 40
void FreeSpawn(PROCESS_INFORMATION *pPI, HANDLE hRd, HANDLE hWr);
BOOL InitSpawn(STARTUPINFO &si, HANDLE &hRd, HANDLE &hWr);
int SetArgv(const TCHAR *cmdLine, TCHAR ***argv);
void SetTitle(HWND hwnd,const TCHAR *substr);
void SetBranding(HWND hwnd);

View file

@ -87,7 +87,7 @@ int main( int argc, char *argv[] )
WARNING: The subprocess can't parse GetCommandLine()
to find its own path since we pass the wrong path! */
if ( CreateProcess( szPath, GetCommandLine(), NULL, NULL,
FALSE, 0, NULL, NULL, &si, &pi ) )
TRUE, 0, NULL, NULL, &si, &pi ) )
{
WaitForSingleObject( pi.hProcess, INFINITE );
GetExitCodeProcess( pi.hProcess, (DWORD*) &err );

View file

@ -12,7 +12,9 @@
\define{NsisACPcp} system default ANSI codepage (ACP)
\define{NsisInputCharset} ACP|OEM|CP#|UTF8|UTF16LE|UTF16BE
\define{NsisInputCharset} ACP|OEM|CP#|UTF8|UTF16<LE|BE>
\define{NsisOutputCharset} ACP|OEM|CP#|UTF8[SIG]|UTF16<LE|BE>[BOM]
\define{NsisWarnBlockContainerBegin} \\<div class=\\"wb\\"\\>

View file

@ -31,6 +31,8 @@ If you want to use MakeNSIS on the command line, the syntax of the makensis comm
\b /INPUTCHARSET allows you to specify a specific codepage for files without a BOM. (\NsisInputCharset)
\b /OUTPUTCHARSET allows you to specify the codepage used by stdout when the output is redirected. (\NsisOutputCharset)
\b Using the /D switch one or more times will add to symbols to the globally defined list (See !define).
\b Using the /X switch one or more times will execute the code you specify following it. Example: "/XAutoCloseWindow false"

View file

@ -26,7 +26,6 @@ makensis_files = Split("""
tstring.cpp
utf.cpp
util.cpp
validateunicode.cpp
winchar.cpp
writer.cpp
""")
@ -71,6 +70,7 @@ AddZLib(env, env['PLATFORM'], 'install-compiler')
##### Defines
env.Append(CPPDEFINES = ['MAKENSIS'])
env.Append(CPPDEFINES = ['_WIN32_IE=0x0500'])
##### Set PCH

View file

@ -474,8 +474,7 @@ int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, UINT codepage/
init_shellconstantvalues();
if ((unsigned)-2 == codepage)
{
assert(curlinereader);
codepage = curlinereader->StreamEncoding().GetCodepage();
codepage = curlinereader ? curlinereader->StreamEncoding().GetCodepage() : CP_UTF8;
// If the current source file is Unicode we have to pick a real codepage for ANSI!
// It might not be the correct codepage but its the best we can do.
// Not using CP_ACP to avoid heisenbugs when compiled on a different system.

View file

@ -80,6 +80,14 @@ namespace MakensisAPI {
NOTIFY_ERROR,
NOTIFY_OUTPUT // generated .exe file
};
#ifdef _WIN32
enum sndmsg_e {
QUERYHOST = WM_APP // QUERYHOST_e in wParam
};
enum QUERYHOST_e {
QH_OUTPUTCHARSET = 1 // return (wincodepage+1) or 0 for default
};
#endif
}
#define PAGE_CUSTOM 0

View file

@ -36,12 +36,24 @@
using namespace std;
int g_noconfig=0;
bool g_dopause=false;
int g_display_errors=1;
FILE *g_output=stdout;
FILE *g_output;
#ifdef _WIN32
UINT g_wincon_orgoutcp;
#ifdef _UNICODE
UINT g_initialCodepage;
WINSIO_OSDATA g_osdata_stdout;
#endif
#endif
static void dopause(void)
{
if (!g_dopause) return;
if (g_display_errors) _ftprintf(g_output,_T("MakeNSIS done - hit enter to close..."));
fflush(stdout);
int a;
while ((a=_gettchar()) != _T('\r') && a != _T('\n') && a != 27/*esc*/);
}
void quit()
{
@ -59,9 +71,7 @@ static void myatexit()
ResetPrintColor();
if (g_output != stdout && g_output) fclose(g_output);
#ifdef _WIN32
#ifdef _UNICODE
SetConsoleOutputCP(g_initialCodepage);
#endif
SetConsoleOutputCP(g_wincon_orgoutcp);
#endif
}
@ -160,6 +170,7 @@ static void print_usage()
_T(" ") OPT_STR _T("NOCONFIG disables inclusion of <path to makensis.exe>") PLATFORM_PATH_SEPARATOR_STR _T("nsisconf.nsh\n")
_T(" ") OPT_STR _T("NOCD disabled the current directory change to that of the .nsi file\n")
_T(" ") OPT_STR _T("INPUTCHARSET <") TSTR_INPUTCHARSET _T(">\n")
_T(" ") OPT_STR _T("OUTPUTCHARSET <") TSTR_OUTPUTCHARSET _T(">\n")
_T(" ") OPT_STR _T("Ddefine[=value] defines the symbol \"define\" for the script [to value]\n")
_T(" ") OPT_STR _T("Xscriptcmd executes scriptcmd in script (i.e. \"") OPT_STR _T("XOutFile poop.exe\")\n")
_T(" ") _T(" parameters are processed by order (") OPT_STR _T("Ddef ins.nsi != ins.nsi ") OPT_STR _T("Ddef)\n")
@ -242,11 +253,11 @@ static int change_to_script_dir(CEXEBuild& build, tstring& script)
return 0;
}
static inline bool HasReqParam(TCHAR**argv,int argi,int argc)
static inline bool HasReqParam(TCHAR**argv,int argi,int argc,bool silent=false)
{
if (argi>=argc || !*argv[argi])
{
PrintColorFmtMsg_ERR(_T("Error: Missing required parameter!\n"));
if (!silent) PrintColorFmtMsg_ERR(_T("Error: Missing required parameter!\n"));
return false;
}
return true;
@ -263,29 +274,111 @@ int _tmain(int argc, TCHAR **argv)
#ifdef NSIS_HPUX_ALLOW_UNALIGNED_DATA_ACCESS
allow_unaligned_data_access();
#endif
assert(sizeof(wchar_t) > 1 && sizeof(wchar_t) <= 4 && sizeof(WORD) == 2);
HWND hostnotifyhandle=0;
const TCHAR*stdoutredirname=0;
NStreamEncoding inputenc, outputenc;
int argpos=0;
bool in_files=false;
bool do_cd=true;
bool no_logo=false;
bool initialparsefail=false;
bool noconfig=false;
#ifdef _WIN32
signed char outputbom=1;
#endif
// Some parameters have to be parsed early so we can initialize stdout and the "host API".
while (++argpos < argc && !initialparsefail)
{
if (!IS_OPT(argv[argpos])) break; // must be a filename, stop parsing
if (!_tcscmp(argv[argpos], _T("--"))) break; // stop parsing
if (_T('-') == argv[argpos][0] && !argv[argpos][1]) continue; // stdin
const TCHAR *swname = &argv[argpos][1];
if (!_tcsicmp(swname,_T("VERSION"))) argc=0;
else if (!_tcsicmp(swname,_T("NOTIFYHWND")))
{
initialparsefail=!HasReqParam(argv,++argpos,argc,true);
if (initialparsefail) break;
hostnotifyhandle=(HWND)_ttol(argv[argpos]);
#ifdef _WIN32
if (!IsWindow(hostnotifyhandle)) hostnotifyhandle=0;
#endif
}
else if (!_tcsicmp(swname,_T("OUTPUTCHARSET")) || !_tcsicmp(swname,_T("OCS")))
{
initialparsefail=!HasReqParam(argv,++argpos,argc,true);
if (initialparsefail) break;
#ifdef _WIN32
bool bom;
WORD cp=GetEncodingFromString(argv[argpos],bom);
if (NStreamEncoding::UNKNOWN == cp)
{
++initialparsefail;
}
else
{
outputbom=bom ? 1 : -1;
outputenc.SetCodepage(cp);
}
#else
outputenc.SetCodepage(NStreamEncoding::UNKNOWN);
#endif
}
#ifdef _WIN32
else if (!_tcsicmp(swname,_T("RAW")))
{
// Emulate the scratchpaper.com fork and its /RAW switch.
// NOTE: Unlike the fork, we print \r\n and not just \n.
outputbom=0;
outputenc.SetCodepage(NStreamEncoding::UTF16LE);
}
#endif
else if (S7IsChEqualI('v',swname[0]) && swname[1] && !swname[2])
{
no_logo=swname[1] >= _T('0') && swname[1] <= _T('2');
}
// This must be parsed last because it will eat other switches
else if (S7IsChEqualI('o',swname[0]) && swname[1]) stdoutredirname=swname+1;
}
#ifdef _WIN32
g_wincon_orgoutcp = GetConsoleOutputCP();
#endif
init_signals(hostnotifyhandle);
FILE*stdoutredir=stdout;
if (stdoutredirname) stdoutredir=my_fopen(stdoutredirname,"w");
g_output=stdoutredir;
if (!g_output) g_output=stdout;
#if defined(_WIN32) && defined(_UNICODE)
if (hostnotifyhandle)
{
// The host can override the output format if they want to
LPARAM lp=MAKELONG(outputenc.GetCodepage(),outputbom);
LRESULT mr=SendMessage(hostnotifyhandle,MakensisAPI::QUERYHOST,MakensisAPI::QH_OUTPUTCHARSET,lp);
if (mr) outputenc.SetCodepage((WORD)--mr), outputbom = -1;
}
if (!WinStdIO_OStreamInit(g_osdata_stdout,g_output,outputenc.GetCodepage(),outputbom))
{
assert(!"StdIO init failed");
return 1;
}
#endif
// g_output is now initialized and Print*/_[f]tprintf can be used
if (!stdoutredir) PrintColorFmtMsg_WARN(_T("Error opening output log for writing! Using stdout.\n"));
unsigned int nousage=0;
unsigned int files_processed=0;
unsigned int cmds_processed=0;
CEXEBuild build;
NStreamEncoding inputenc;
bool outputtried=0;
bool in_files=0;
bool do_cd=1;
bool no_logo=0;
int nousage=0;
int argpos=1;
int tmpargpos=1;
int files_processed=0;
int cmds_processed=0;
#ifdef _UNICODE
#ifndef _O_U8TEXT
const int _O_U8TEXT=0x40000; // BUGBUG: This is bogus (Makensis will ONLY work on NT6)
#endif
_setmode(_fileno(stdout), _O_U8TEXT); // set stdout to UTF-8
#ifdef _WIN32
g_initialCodepage = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8); // set console output to UTF-8 (especially useful for subprocesses like !system)
#endif
#endif
try
{
build.initialize(argv[0]);
@ -296,66 +389,107 @@ int _tmain(int argc, TCHAR **argv)
return 1;
}
if (argc > 1 && IS_OPT(argv[tmpargpos]) && !_tcsicmp(&argv[tmpargpos][1],_T("VERSION")))
#ifdef _WIN32
build.notify_hwnd=hostnotifyhandle;
#else
const TCHAR*const badnonwinswitchfmt=OPT_STR _T("%s is disabled for non Win32 platforms.");
if (hostnotifyhandle)
build.warning(badnonwinswitchfmt,_T("NOTIFYHWND"));
if (NStreamEncoding::UNKNOWN==outputenc.GetCodepage())
build.warning(badnonwinswitchfmt,_T("OUTPUTCHARSET"));
#endif // ~_WIN32
if (!argc)
{
_ftprintf(g_output,NSIS_VERSION);
fflush(g_output);
return 0;
}
if (argc > 1 && IS_OPT(argv[tmpargpos]) && S7IsChEqualI('v',argv[tmpargpos][1]))
{
if (argv[tmpargpos][2] <= _T('2') && argv[tmpargpos][2] >= _T('0'))
{
no_logo=1;
}
tmpargpos++;
}
if (!no_logo)
{
if (argc > tmpargpos && IS_OPT(argv[tmpargpos]) && S7IsChEqualI('o',argv[tmpargpos][1]) && argv[tmpargpos][2])
{
g_output=FOPENTEXT(argv[tmpargpos]+2,"w");
if (!g_output)
{
g_output=stdout; // Needs to be set before calling PrintColorFmtMsg*
PrintColorFmtMsg_WARN(_T("Error opening output log for writing. Using stdout.\n"));
}
outputtried=1;
}
print_logo();
}
if (!g_output) g_output=stdout;
// Look for /NOTIFYHWND so we can init_signals()
const int orgargpos=argpos;
while (argpos < argc)
{
if (!_tcscmp(argv[argpos], _T("--"))) break;
if (!IS_OPT(argv[argpos]) || !_tcscmp(argv[argpos], _T("-"))) break;
if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND")))
{
if (!HasReqParam(argv, ++argpos, argc)) break;
#ifdef _WIN32
build.notify_hwnd=(HWND)_ttol(argv[argpos]);
if (!IsWindow(build.notify_hwnd)) build.notify_hwnd=0;
#else
build.warning(OPT_STR _T("NOTIFYHWND is disabled for non Win32 platforms."));
#endif
}
argpos++;
}
argpos=orgargpos;
if (!no_logo) print_logo();
init_signals(build.notify_hwnd);
argpos=initialparsefail ? argc : 1;
while (argpos < argc)
{
if (!_tcscmp(argv[argpos], _T("--")))
in_files=1;
else if (IS_OPT(argv[argpos]) && _tcscmp(argv[argpos], _T("-")) && !in_files)
{
if (S7IsChEqualI('d',argv[argpos][1]) && argv[argpos][2])
if (!_tcsicmp(&argv[argpos][1],_T("NOCD"))) do_cd=false;
else if (!_tcsicmp(&argv[argpos][1],_T("NOCONFIG"))) noconfig=true;
else if (!_tcsicmp(&argv[argpos][1],_T("PAUSE"))) g_dopause=true;
else if (!_tcsicmp(&argv[argpos][1],_T("LICENSE")))
{
if (build.display_info) print_license();
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("CMDHELP")))
{
if (argpos < argc-1)
build.print_help(argv[++argpos]);
else
build.print_help(NULL);
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("HDRINFO")))
{
print_stub_info(build);
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("INPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("ICS")))
{
if (!HasReqParam(argv, ++argpos, argc)) break;
WORD cp = GetEncodingFromString(argv[argpos]);
if (NStreamEncoding::UNKNOWN == cp)
{
if (_tcsicmp(argv[argpos], _T("AUTO")))
build.warning(OPT_STR _T("INPUTCHARSET: Ignoring invalid charset %s"), argv[argpos]);
cp = NStreamEncoding::AUTO;
}
inputenc.SafeSetCodepage(cp);
}
else if (S7IsChEqualI('v',argv[argpos][1]) &&
argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('4') && !argv[argpos][3])
{
int v=argv[argpos][2]-_T('0');
build.display_script=v>3;
build.display_info=v>2;
build.display_warnings=v>1;
build.display_errors=v>0;
g_display_errors=build.display_errors;
}
else if (S7IsChEqualI('p',argv[argpos][1]) &&
argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('5') && !argv[argpos][3])
{
#ifdef _WIN32
// priority setting added 01-2007 by Comm@nder21
int p=argv[argpos][2]-_T('0');
HANDLE hProc = GetCurrentProcess();
struct
{
DWORD priority, fallback;
} static const classes[] = {
{IDLE_PRIORITY_CLASS, IDLE_PRIORITY_CLASS},
{BELOW_NORMAL_PRIORITY_CLASS, IDLE_PRIORITY_CLASS},
{NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS},
{ABOVE_NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS},
{HIGH_PRIORITY_CLASS, HIGH_PRIORITY_CLASS},
{REALTIME_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS}
};
if (SetPriorityClass(hProc, classes[p].priority) == FALSE)
{
SetPriorityClass(hProc, classes[p].fallback);
}
if (p == 5) build.warning(_T("makensis is running in REALTIME priority mode!"));
#else
build.warning(badnonwinswitchfmt,_T("Px"));
#endif
}
// Already parsed these (must adjust argpos)
else if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND"))) ++argpos;
else if (!_tcsicmp(&argv[argpos][1],_T("OUTPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("OCS"))) ++argpos;
// These must be parsed last because they will eat other switches
else if (S7IsChEqualI('d',argv[argpos][1]) && argv[argpos][2])
{
TCHAR *p=argv[argpos]+2;
TCHAR *s=_tcsdup(p),*v;
@ -373,102 +507,12 @@ int _tmain(int argc, TCHAR **argv)
}
cmds_processed++;
}
else if (S7IsChEqualI('o',argv[argpos][1]) && argv[argpos][2])
{
if (!outputtried)
{
g_output=FOPENTEXT(argv[argpos]+2,"w");
if (!g_output)
{
g_output=stdout; // Needs to be set before calling PrintColorFmtMsg*
if (build.display_errors) PrintColorFmtMsg_WARN(_T("Error opening output log for writing. Using stdout.\n"));
}
outputtried=1;
}
}
else if (!_tcsicmp(&argv[argpos][1],_T("NOCD"))) do_cd=0;
else if (S7IsChEqualI('v',argv[argpos][1]) &&
argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('4') && !argv[argpos][3])
{
int v=argv[argpos][2]-_T('0');
build.display_script=v>3;
build.display_info=v>2;
build.display_warnings=v>1;
build.display_errors=v>0;
g_display_errors=build.display_errors;
}
else if (!_tcsicmp(&argv[argpos][1],_T("NOCONFIG"))) g_noconfig=1;
else if (!_tcsicmp(&argv[argpos][1],_T("PAUSE"))) g_dopause=1;
else if (!_tcsicmp(&argv[argpos][1],_T("LICENSE")))
{
if (build.display_info)
{
print_license();
}
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("CMDHELP")))
{
if (argpos < argc-1)
build.print_help(argv[++argpos]);
else
build.print_help(NULL);
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND")))
{
++argpos; // already parsed this
}
else if (!_tcsicmp(&argv[argpos][1],_T("HDRINFO")))
{
print_stub_info(build);
nousage++;
}
else if (S7IsChEqualI('p',argv[argpos][1]) &&
argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('5') && !argv[argpos][3])
{
// Already parsed these
#ifdef _WIN32
// priority setting added 01-2007 by Comm@nder21
int p=argv[argpos][2]-_T('0');
HANDLE hProc = GetCurrentProcess();
struct
{
DWORD priority;
DWORD fallback;
} classes[] = {
{IDLE_PRIORITY_CLASS, IDLE_PRIORITY_CLASS},
{BELOW_NORMAL_PRIORITY_CLASS, IDLE_PRIORITY_CLASS},
{NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS},
{ABOVE_NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS},
{HIGH_PRIORITY_CLASS, HIGH_PRIORITY_CLASS},
{REALTIME_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS}
};
if (SetPriorityClass(hProc, classes[p].priority) == FALSE)
{
SetPriorityClass(hProc, classes[p].fallback);
}
if (p == 5)
build.warning(_T("makensis is running in REALTIME priority mode!"));
#else
build.warning(OPT_STR _T("Px is disabled for non Win32 platforms."));
else if (!_tcsicmp(&argv[argpos][1],_T("RAW"))) {}
#endif
}
else if (!_tcsicmp(&argv[argpos][1],_T("INPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("ICS")))
{
if (!HasReqParam(argv, ++argpos, argc)) break;
WORD cp = GetEncodingFromString(argv[argpos]);
if (NStreamEncoding::UNKNOWN == cp)
{
if (_tcsicmp(argv[argpos], _T("AUTO")))
build.warning(OPT_STR _T("INPUTCHARSET: Ignoring invalid charset %s"), argv[argpos]);
cp = NStreamEncoding::AUTO;
}
inputenc.SafeSetCodepage(cp);
}
else if (!_tcsicmp(&argv[argpos][1],_T("VERSION"))) {}
else if (S7IsChEqualI('o',argv[argpos][1]) && argv[argpos][2]) {}
else
break;
}
@ -476,10 +520,10 @@ int _tmain(int argc, TCHAR **argv)
{
files_processed++;
if (!_tcscmp(argv[argpos],_T("-")) && !in_files)
g_dopause=0;
if (!g_noconfig)
g_dopause=false;
if (!noconfig)
{
g_noconfig=1;
noconfig=true;
tstring main_conf;
TCHAR* env_var = _tgetenv(_T("NSISCONFDIR"));
if(env_var == NULL)

View file

@ -3241,6 +3241,9 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
TCHAR *exec=line.gettoken_str(1);
SCRIPT_MSG(_T("!execute: \"%s\"\n"),exec);
#ifdef _WIN32
#ifdef _UNICODE
RunChildProcessRedirected(0,exec);
#else
PROCESS_INFORMATION pi;
STARTUPINFO si={sizeof(STARTUPINFO),};
if (CreateProcess(NULL,exec,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
@ -3249,6 +3252,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
#endif
#else
TCHAR *execfixed = my_convert(exec);
system(execfixed);

View file

@ -15,91 +15,10 @@
#ifdef _UNICODE
#include "tstring.h"
#include "validateunicode.h"
#include "util.h"
#include <vector>
#include <stdio.h>
FILE* FileOpenUnicodeText(const TCHAR* file, const TCHAR* mode, BOOL* unicode)
{
extern FILE *g_output;
CValidateUnicode::FILE_TYPE ftype = CValidateUnicode::UTF_8; // default file format is UTF-8
if (unicode) *unicode = TRUE;
// If we are reading an existing file, check to see what type of file it
// is first.
if (_tcsstr(mode, _T("w+")) ||
_tcsstr(mode, _T("r")))
{
FILE* fp = _tfopen(file, _T("rb"));
if (fp)
{
MANAGE_WITH(fp, fclose);
fseek(fp, 0, SEEK_END);
size_t fileSize = ftell(fp);
if (fileSize == 0)
{
// Empty files are treated as UTF-8.
ftype = CValidateUnicode::UTF_8;
}
else
{
std::vector<unsigned char> buffer(fileSize);
fseek(fp, 0, SEEK_SET);
fread(&buffer[0], sizeof(unsigned char), fileSize, fp);
ftype = CValidateUnicode::CheckBOM(&buffer[0], buffer.size());
switch (ftype)
{
case CValidateUnicode::UTF_8:
case CValidateUnicode::UTF_16LE:
case CValidateUnicode::UTF_16BE:
break;
case CValidateUnicode::UTF_32LE:
case CValidateUnicode::UTF_32BE:
PrintColorFmtMsg_ERR(_T("File '%s' has a BOM marked as %s which is not supported at this time.\n"),
file, CValidateUnicode::TypeToName(ftype));
exit(-1);
break;
case CValidateUnicode::UNKNOWN:
// If unknown, let's see if it's not just UTF_8 without a BOM.
if (CValidateUnicode::ValidateUTF8(&buffer[0], buffer.size()) == 2)
{
// contains UTF-8 characters sequences
_ftprintf(g_output, _T("File '%s' has no BOM but seems to be UTF-8.\n"), file);
ftype = CValidateUnicode::UTF_8;
}
break;
default:
PrintColorFmtMsg_ERR(_T("CValidateUnicode::CheckBOM() for file '%s' returned an unknown return value: %d\n"),
file, ftype);
exit(-1);
break;
}
}
}
}
tstring strMode(mode);
switch (ftype)
{
case CValidateUnicode::UTF_8:
strMode.append(_T(", ccs=UTF-8"));
break;
case CValidateUnicode::UTF_16LE:
strMode.append(_T(", ccs=UTF-16LE"));
break;
default:
// Looks like fopen() doesn't support other encodings of Unicode.
if (unicode) *unicode = FALSE;
break;
}
return _tfopen(file, strMode.c_str());
}
CtoTString::CtoTString(const char* str)
{

View file

@ -28,17 +28,10 @@
typedef std::wstring tstring;
typedef std::wofstream tofstream;
typedef std::wifstream tifstream;
// Use the following macros to open text files.
FILE* FileOpenUnicodeText(const TCHAR* file, const TCHAR* mode, BOOL* unicode);
#define FOPENTEXT(file, mode) FileOpenUnicodeText(file, _T(mode), NULL)
#define FOPENTEXT2(file, mode, unicode) FileOpenUnicodeText(file, _T(mode), unicode)
#else
typedef std::string tstring;
typedef std::ofstream tofstream;
typedef std::ifstream tifstream;
// Use the following macros to open text files.
#define FOPENTEXT(file, mode) fopen(file, mode)
#define FOPENTEXT2(file, mode, unicode) (*(unicode)=FALSE, fopen(file, mode))
#endif
#ifndef _UNICODE

View file

@ -16,6 +16,7 @@
*/
#include "utf.h"
#include "util.h"
#define FIX_ENDIAN_INT16LETOHOST_INPLACE FIX_ENDIAN_INT16_INPLACE
@ -259,10 +260,27 @@ bool WCToUTF16LEHlpr::Create(const TCHAR*in)
}
#endif
UINT DetectUTFBOM(void*Buffer, UINT cb)
{
unsigned char *b = (unsigned char*) Buffer;
if (cb >= 3 && 0xef == b[0] && 0xbb == b[1] && 0xbf == b[2])
return NStreamEncoding::UTF8;
if (cb >= 2)
{
if (cb >= 4 && !b[0] && !b[1] && 0xfe == b[2] && 0xff == b[3])
return NStreamEncoding::UTF32BE;
if (0xff == b[0] && 0xfe == b[1])
return (cb >= 4 && !b[2] && !b[3]) ? NStreamEncoding::UTF32LE : NStreamEncoding::UTF16LE;
if (0xfe == b[0] && 0xff == b[1])
return NStreamEncoding::UTF16BE;
}
return 0;
}
UINT DetectUTFBOM(FILE*strm)
{
/*\
Tries to detect a BOM at the start of a stream. If a BOM is found it is eaten.
Tries to detect a BOM at the current position in a stream.
If a BOM is found it is eaten.
NOTE: ungetc is only guaranteed to support 1 pushback,
lets hope no MBCS file starts with parts of a BOM.
\*/
@ -358,7 +376,8 @@ bool NBaseStream::Attach(FILE*hFile, WORD enc, bool Seek /*= true*/)
{
Close();
m_hFile = hFile;
if (!m_hFile || !NStream::SetBinaryMode(m_hFile)) return false;
if (!m_hFile) return false;
if (!NStream::SetBinaryMode(m_hFile) && m_hFile != stdin) return false;
fpos_t pos;
if (Seek && !fgetpos(m_hFile, &pos)) rewind(m_hFile); else Seek = false;
WORD cp = DetectUTFBOM(m_hFile);
@ -377,10 +396,36 @@ bool NOStream::WriteString(const wchar_t*Str, size_t cch /*= -1*/)
CharEncConv cec;
if (!cec.Initialize(m_Enc.GetCodepage(), -1)) return false;
cec.SetAllowOptimizedReturn(true);
if ((unsigned)-1 != cch) cch *= sizeof(wchar_t); // cec.Convert wants byte count
size_t cbConv;
char *p = (char*) cec.Convert(Str, cch, &cbConv);
return p && WriteOctets(p, cbConv);
}
bool NOStream::WritePlatformNLString(const wchar_t*Str, size_t cch /*= -1*/)
{
#ifdef _WIN32
size_t cch2 = 0, nlcount = 0;
for(; cch2 < cch && Str[cch2]; ++cch2) if (L'\n' == Str[cch2]) ++nlcount;
if (nlcount)
{
cch = cch2 + nlcount;
wchar_t chPrev = 0, *buf = (wchar_t*) malloc(cch * sizeof(wchar_t));
if (!buf) return false;
for(size_t s = 0, d = 0; d < cch; ++s, ++d)
{
if (L'\n' == Str[s])
{
if (L'\r' != chPrev) buf[d++] = L'\r'; else --cch;
}
buf[d] = chPrev = Str[s];
}
bool retval = WriteString(buf, cch);
free(buf);
return retval;
}
#endif
return WriteString(Str, cch);
}
tstring NStreamLineReader::GetErrorMessage(UINT Error, const TCHAR*Filename, UINT Line)
{
@ -450,26 +495,7 @@ l_restart:
else
#endif
{
if (0xC0 == (0xC0 & chU8[0]))
{
++cb;
if (0xE0 == (0xE0 & chU8[0]))
{
++cb;
if (0xF0 == (0xF0 & chU8[0]))
{
++cb;
if (0xF8 == (0xF8 & chU8[0]))
{
++cb;
if (0xFC == (0xFE & chU8[0]))
++cb;
else
goto l_badutf;
}
}
}
}
if (!UTF8_GetTrailCount(chU8[0], cb)) goto l_badutf;
for(BYTE moreU8 = 0; moreU8 < cb;)
{
BYTE b;

View file

@ -19,22 +19,12 @@
#define NSIS_UTF_H
#include "Platform.h"
#include <stdlib.h>
#include <stdio.h>
#include "util.h" // For my_fopen
#ifdef _WIN32
#include <io.h> // For _setmode
#include <fcntl.h> // For _O_BINARY
#endif
const WORD UNICODE_REPLACEMENT_CHARACTER = 0xfffd;
#define TSTR_INPUTCHARSET _T("ACP|OEM|CP#|UTF8|UTF16<LE|BE>")
#define TSTR_OUTPUTCHARSET _T("ACP|OEM|CP#|UTF8[SIG]|UTF16<LE|BE>[BOM]")
void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch);
template<typename T> T S7ChLwr(T c) { return c>='A' && c<='Z' ? (T)(c|32) : c; }
template<typename T> T S7ChUpr(T c) { return c>='a' && c<='z' ? (T)(c-'a'+'A') : c; }
template<typename T> bool S7IsChEqualI(char ch,T cmp)
@ -58,12 +48,51 @@ inline UINT32 CodePointFromUTF16SurrogatePair(unsigned short lea,unsigned short
return ((UINT32)lea << 10) + tra + surrogate_offset;
}
inline bool UTF8_GetTrailCount(unsigned char chFirst, unsigned char &cb)
{
// This function should only be used to get a rough idea of how large the encoded
// codepoint is, just because it returns true does not mean that it is valid UTF-8!
cb = 0;
if (0xC0 == (0xC0 & chFirst))
{
++cb;
if (0xE0 == (0xE0 & chFirst))
{
++cb;
if (0xF0 == (0xF0 & chFirst))
{
++cb;
if (0xF8 == (0xF8 & chFirst))
{
++cb;
if (0xFC == (0xFE & chFirst)) ++cb; else return false;
}
}
}
}
return true;
}
#ifdef MAKENSIS
#include <stdlib.h>
#include <stdio.h>
#include "tstring.h"
#ifdef _WIN32
#include <io.h> // For _setmode
#include <fcntl.h> // For _O_BINARY
#endif
FILE* my_fopen(const TCHAR *path, const char *mode); // from util.h
void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch);
void UTF16InplaceEndianSwap(void*Buffer, UINT cch);
UINT StrLenUTF16(const void*str);
bool StrSetUTF16LE(tstring&dest, const void*src);
UINT WCFromCodePoint(wchar_t*Dest,UINT cchDest,UINT32 CodPt);
wchar_t* DupWCFromBytes(void*Buffer,UINT cbBuffer,WORD SrcCP);
UINT DetectUTFBOM(void*Buffer,UINT cb);
UINT DetectUTFBOM(FILE*strm);
WORD GetEncodingFromString(const TCHAR*s, bool&BOM);
WORD GetEncodingFromString(const TCHAR*s);
@ -220,8 +249,9 @@ protected:
NStreamEncoding m_Enc;
public:
NBaseStream() : m_hFile(0) {}
NBaseStream(FILE *hFile = 0) : m_hFile(hFile) {}
~NBaseStream() { Close(); }
FILE* GetHandle() const { return m_hFile; }
NStreamEncoding& StreamEncoding() { return m_Enc; }
bool IsEOF() const { return feof(m_hFile) != 0; }
@ -280,6 +310,8 @@ public:
class NOStream : public NBaseStream {
public:
NOStream(FILE *hFile = 0) : NBaseStream(hFile) {}
bool CreateFileForWriting(const TCHAR* Path, WORD enc = NStreamEncoding::AUTO)
{
return Attach(my_fopen(Path, "w+b"), enc);
@ -314,6 +346,7 @@ public:
return false;
}
bool WriteString(const wchar_t*Str, size_t cch = -1);
bool WritePlatformNLString(const wchar_t*Str, size_t cch = -1);
};
class NStreamLineReader {
@ -343,4 +376,5 @@ protected:
}
};
#endif // MAKENSIS
#endif // NSIS_UTF_H

View file

@ -26,6 +26,7 @@
#include "util.h"
#include "strlist.h"
#include "winchar.h"
#include "utf.h"
#ifndef _WIN32
# include <ctype.h>
@ -50,21 +51,9 @@ namespace Apple { // defines struct section
using namespace std;
int g_dopause=0;
extern int g_display_errors;
extern FILE *g_output;
void dopause(void)
{
if (g_dopause)
{
if (g_display_errors) _ftprintf(g_output,_T("MakeNSIS done - hit enter to close..."));
fflush(stdout);
int a;
while ((a=_gettchar()) != _T('\r') && a != _T('\n') && a != 27/*esc*/);
}
}
double my_wtof(const wchar_t *str)
{
char buf[100];
@ -639,8 +628,119 @@ size_t ExpandoStrFmtVaList(wchar_t*Stack, size_t cchStack, wchar_t**ppMalloc, co
return cch;
}
#if defined(_WIN32) && defined(_UNICODE)
int RunChildProcessRedirected(LPCWSTR cmdprefix, LPCWSTR cmdmain)
{
// We have to deliver the requested output encoding to our host (if any) and the
// only way to do that is to convert the pipe content from what we hope is UTF-8.
// The reason we need a pipe in the first place is because we cannot trust the
// child to call GetConsoleOutputCP(), and even if we could, UTF-16 is not valid there.
UINT cp = CP_UTF8, mbtwcf = MB_ERR_INVALID_CHARS;
errno = ENOMEM;
if (!cmdprefix) cmdprefix = _T("");
UINT cch1 = _tcslen(cmdprefix), cch2 = _tcslen(cmdmain);
WCHAR *cmd = (WCHAR*) malloc( (cch1 + cch2 + 1) * sizeof(WCHAR) );
if (!cmd) return -1;
_tcscpy(cmd, cmdprefix);
_tcscat(cmd, cmdmain);
SECURITY_DESCRIPTOR sd = {1, 0, SE_DACL_PRESENT, NULL, };
SECURITY_ATTRIBUTES sa = {sizeof(sa), &sd, true};
const UINT orgwinconcp = GetConsoleCP(), orgwinconoutcp = GetConsoleOutputCP();
HANDLE hPipRd, hPipWr;
PROCESS_INFORMATION pi;
BOOL ok = CreatePipe(&hPipRd, &hPipWr, &sa, 0);
if (!ok)
hPipRd = 0, hPipWr = 0;
else
{
STARTUPINFO si = {sizeof(si)};
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = si.hStdError = hPipWr;
si.hStdInput = INVALID_HANDLE_VALUE;
errno = ECHILD;
SetConsoleOutputCP(cp);
ok = CreateProcess(0, cmd, 0, 0, TRUE, 0, 0, 0, &si, &pi);
CloseHandle(hPipWr); // We want ERROR_BROKEN_PIPE when the child is done
}
free(cmd);
DWORD childec = -1;
if (ok)
{
bool utf8 = true, okt;
char iobuf[512];
DWORD cbRead, cbOfs = 0, cchwb = 0;
WCHAR wbuf[100], wchbuf[2+1]; // A surrogate pair + \0
for(;;)
{
BOOL okr = ReadFile(hPipRd, iobuf+cbOfs, sizeof(iobuf)-cbOfs, &cbRead, 0);
cbRead += cbOfs, cbOfs = 0;
unsigned char cbTrail, cch;
for(DWORD i = 0; i < cbRead;)
{
cch = 0;
if (utf8)
{
okt = UTF8_GetTrailCount(iobuf[i], cbTrail);
if (!okt) // Not UTF-8? Switching to ACP
{
switchtoacp:cp = CP_ACP, mbtwcf = 0, utf8 = false;
SetConsoleOutputCP(cp);
continue;
}
if (!cbTrail) cch++, wchbuf[0] = iobuf[i]; // ASCII
}
else
{
cbTrail = !!IsDBCSLeadByteEx(cp, iobuf[i]);
}
if (i+cbTrail >= cbRead) // Read more first?
{
memmove(iobuf, iobuf+i, cbOfs = cbRead - i);
if (okr) break; else i = 0;
}
if (!cch)
{
cch = MultiByteToWideChar(cp, mbtwcf, &iobuf[i], 1+cbTrail, wchbuf, COUNTOF(wchbuf)-1);
if (!cch)
{
if (utf8) goto switchtoacp;
cch++, wchbuf[0] = UNICODE_REPLACEMENT_CHARACTER;
}
}
i += 1+cbTrail;
if (0xfeff == wchbuf[0] && 1 == cch) cch = 0; // MakeNsisW is not a fan of the BOM, eat it.
if (!cch) continue;
wbuf[cchwb++] = wchbuf[0];
if (--cch) wbuf[cchwb++] = wchbuf[1];
const bool fullbuf = cchwb+cch >= COUNTOF(wbuf)-1; // cch is 1 for surrogate pairs
if (!okr || fullbuf || L'\n' == wchbuf[0]) // Stop on \n so \r\n conversion has enough context (...\r\n vs ...\n)
{
#ifdef MAKENSIS
extern WINSIO_OSDATA g_osdata_stdout;
WinStdIO_OStreamWrite(g_osdata_stdout, wbuf, cchwb); // Faster than _ftprintf
#else
wbuf[cchwb] = L'\0';
_ftprintf(g_output, _T("%s"), wbuf);
#endif
cchwb = 0;
}
}
if (!okr) break;
}
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &childec);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
SetConsoleCP(orgwinconcp); SetConsoleOutputCP(orgwinconoutcp);
CloseHandle(hPipRd);
return childec;
}
#endif
int sane_system(const TCHAR *command) {
int sane_system(const TCHAR *command)
{
#ifdef _WIN32
// workaround for bug #1509909
@ -653,17 +753,119 @@ int sane_system(const TCHAR *command) {
// `program files\nsis\makensis.exe" "args`
// which obviously fails...
//
// to avoid the stripping, a harmless string is prefixed
// to the command line.
tstring command_s = _T("IF 1==1 ");
command_s += command;
return _tsystem(command_s.c_str());
// to avoid the stripping, a harmless string is prefixed to the command line.
const TCHAR* prefix = _T("IF 1==1 ");
#ifdef _UNICODE
if (!command) return 0;
if (!*command) return 1;
tstring fixedcmd = _tgetenv(_T("COMSPEC"));
if (!fixedcmd.length()) fixedcmd = _T("CMD.EXE");
fixedcmd += _T(" /C ");
fixedcmd += prefix;
return RunChildProcessRedirected(fixedcmd.c_str(), command);
#else
tstring fixedcmd = prefix + _T("") + command;
return _tsystem(fixedcmd.c_str());
#endif
#else
return _tsystem(command);
#endif
}
#ifdef _WIN32
bool GetFileSize64(HANDLE hFile, ULARGE_INTEGER &uli)
{
uli.LowPart = GetFileSize(hFile, &uli.HighPart);
return INVALID_FILE_SIZE != uli.LowPart || !GetLastError();
}
#endif
#if defined(_WIN32) && defined(_UNICODE) && defined(MAKENSIS)
#include <io.h> // for _get_osfhandle
bool WINAPI WinStdIO_OStreamInit(WINSIO_OSDATA&osd, FILE*strm, WORD cp, int bom)
{
// bom < 0: override cp if UTF detected but never write BOM
// bom = 0: ignore BOM and force cp
// bom > 0: override cp if UTF detected, write BOM if it does not already exist
const int fd = _fileno(strm);
osd.mode = 0, osd.hCRT = strm, osd.hNative = (HANDLE) _get_osfhandle(fd);
if (INVALID_HANDLE_VALUE == osd.hNative) return false;
DWORD conmode;
if (GetConsoleMode(osd.hNative, &conmode)) osd.mode++; else osd.mode--;
bool succ = NStream::SetBinaryMode(fd);
DWORD cbio = 0;
ULARGE_INTEGER uli;
if (succ && GetFileSize64(osd.hNative, uli) && uli.QuadPart)
{
OVERLAPPED olap = {0}; // Used to read from start of file
unsigned char bufbom[4];
if (ReadFile(osd.hNative, bufbom, sizeof(bufbom), &cbio, &olap))
{
UINT detbom = DetectUTFBOM(bufbom, cbio);
if (detbom) cp = (WORD) detbom, bom = 0;
}
SetFilePointer(osd.hNative, 0, 0, FILE_END);
}
osd.mustwritebom = bom > 0 && !cbio, osd.cp = cp;
return succ || (sizeof(TCHAR)-1 && WinStdIO_IsConsole(osd)); // Don't care about BOM for WriteConsoleW
}
bool WINAPI WinStdIO_OStreamWrite(WINSIO_OSDATA&osd, const wchar_t *Str, UINT cch)
{
if ((unsigned)-1 == cch) cch = _tcslen(Str);
DWORD cbio;
if (WinStdIO_IsConsole(osd))
return !!WriteConsoleW(osd.hNative, Str, cch, &cbio, 0) || !cch;
NOStream strm(osd.hCRT);
NStreamEncoding &enc = strm.StreamEncoding();
enc.SetCodepage(osd.cp);
bool retval = false;
if (osd.mustwritebom)
{
osd.mustwritebom = false;
if (enc.IsUnicode() && !strm.WriteBOM(enc))
{
osd.mode = 1, osd.hNative = 0; // Something is wrong, stop writing!
goto end;
}
}
retval = strm.WritePlatformNLString(Str, cch);
end:
strm.Detach();
return retval;
}
int WINAPI WinStdIO_vfwprintf(FILE*strm, const wchar_t*Fmt, va_list val)
{
if (g_output == strm && Fmt)
{
extern WINSIO_OSDATA g_osdata_stdout;
ExpandoString<wchar_t, NSIS_MAX_STRLEN> buf;
errno = ENOMEM;
UINT cch = buf.StrFmt(Fmt, val, false);
if (cch && !WinStdIO_OStreamWrite(g_osdata_stdout, buf, cch))
{
cch = 0, errno = EIO;
}
return cch ? cch : (*Fmt ? -1 : 0);
}
return vfwprintf(strm, Fmt, val);
}
int WinStdIO_fwprintf(FILE*strm, const wchar_t*Fmt, ...)
{
va_list val;
va_start(val, Fmt);
int rv = _vftprintf(strm, Fmt, val);
va_end(val);
return rv;
}
int WinStdIO_wprintf(const wchar_t*Fmt, ...)
{
va_list val;
va_start(val, Fmt);
int rv = _vftprintf(g_output, Fmt, val);
va_end(val);
return rv;
}
#endif
void PrintColorFmtMsg(unsigned int type, const TCHAR *fmtstr, va_list args)
{
@ -693,6 +895,9 @@ gottxtattrbak:
case 1: txtattr = FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED; break;
case 2: txtattr = FOREGROUND_INTENSITY|FOREGROUND_RED; break;
}
// Use original background color if our text will still be readable
if ((contxtattrbak & 0xF0) != (txtattr<<4)) txtattr |= (contxtattrbak & 0xF0);
if ((txtattr & 0xFF) == 0xFE) txtattr &= ~FOREGROUND_INTENSITY; // BrightYellow on BrightWhite is hard to read
SetConsoleTextAttribute(hWin32Con, txtattr);
}
#endif

View file

@ -30,13 +30,9 @@
# include <unistd.h>
#endif
#include <stdarg.h>
#include <stdarg.h>
// these are the standard pause-before-quit stuff.
extern int g_dopause;
extern void dopause(void);
extern double my_wtof(const wchar_t *str);
extern unsigned int my_strncpy(TCHAR*Dest, const TCHAR*Src, unsigned int cchMax);
@ -72,10 +68,10 @@ public:
if (!p) throw std::bad_alloc();
m_heap = (T*) p;
}
size_t StrFmt(const T*FmtStr, va_list Args)
size_t StrFmt(const T*FmtStr, va_list Args, bool throwonerr = true)
{
size_t n = ExpandoStrFmtVaList(m_stack, COUNTOF(m_stack), &m_heap, FmtStr, Args);
if (!n && *FmtStr) throw std::bad_alloc();
if (throwonerr && !n && *FmtStr) throw std::bad_alloc();
return n;
}
T* GetPtr() { return m_heap ? m_heap : m_stack; }
@ -87,6 +83,34 @@ int sane_system(const TCHAR *command);
void PrintColorFmtMsg(unsigned int type, const TCHAR *fmtstr, va_list args);
void FlushOutputAndResetPrintColor();
#ifdef _WIN32
#ifdef _UNICODE
int RunChildProcessRedirected(LPCWSTR cmdprefix, LPCWSTR cmdmain);
#ifdef MAKENSIS
typedef struct {
HANDLE hNative;
FILE*hCRT;
WORD cp;
signed char mode; // -1 = redirected, 0 = unknown, 1 = console
bool mustwritebom;
} WINSIO_OSDATA;
inline bool WinStdIO_IsConsole(WINSIO_OSDATA&osd) { return osd.mode > 0; }
inline bool WinStdIO_IsRedirected(WINSIO_OSDATA&osd) { return osd.mode < 0; }
bool WINAPI WinStdIO_OStreamInit(WINSIO_OSDATA&osd, FILE*strm, WORD cp, int bom = 1);
bool WINAPI WinStdIO_OStreamWrite(WINSIO_OSDATA&osd, const wchar_t *Str, UINT cch = -1);
int WINAPI WinStdIO_vfwprintf(FILE*strm, const wchar_t*Fmt, va_list val);
int WinStdIO_fwprintf(FILE*strm, const wchar_t*Fmt, ...);
int WinStdIO_wprintf(const wchar_t*Fmt, ...);
// We don't hook fflush since the native handle is only used with WriteConsoleW
#undef _vsntprintf
#define _vsntprintf Error: TODO
#undef _tprintf
#define _tprintf WinStdIO_wprintf
#undef _ftprintf
#define _ftprintf WinStdIO_fwprintf
#undef _vftprintf
#define _vftprintf WinStdIO_vfwprintf
#endif // ~MAKENSIS
#endif // ~_UNICODE
#define ResetPrintColor() FlushOutputAndResetPrintColor() // For reset ONLY use PrintColorFmtMsg(0,NULL ...
#define SetPrintColorWARN() PrintColorFmtMsg(1|0x10, NULL, (va_list)NULL)
#define SetPrintColorERR() PrintColorFmtMsg(2|0x10, NULL, (va_list)NULL)
@ -94,7 +118,7 @@ void FlushOutputAndResetPrintColor();
#define ResetPrintColor()
#define SetPrintColorWARN()
#define SetPrintColorERR()
#endif
#endif // ~_WIN32
inline void PrintColorFmtMsg_WARN(const TCHAR *fmtstr, ...)
{
va_list val;