Bug 396052 - Firefox fails to start when run from a directory with non-native characters, r=luser sr=neil

This commit is contained in:
benjamin@smedbergs.us 2007-12-31 07:15:43 -08:00
Родитель 76576ca87c
Коммит 81cceca417
26 изменённых файлов: 496 добавлений и 270 удалений

Просмотреть файл

@ -151,6 +151,13 @@ endif
NSDISTMODE = copy
include $(topsrcdir)/config/config.mk
ifdef _MSC_VER
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
endif
ifndef BUILD_STATIC_LIBS
ifdef NS_TRACE_MALLOC

Просмотреть файл

@ -53,6 +53,11 @@
#include "nsILocalFile.h"
#include "nsStringGlue.h"
#ifdef XP_WIN
// we want a wmain entry point
#include "nsWindowsWMain.cpp"
#endif
static void Output(const char *fmt, ... )
{
va_list ap;
@ -156,13 +161,3 @@ int main(int argc, char* argv[])
PR_smprintf_free(appEnv);
return result;
}
#if defined( XP_WIN ) && defined( WIN32 ) && !defined(__GNUC__)
// We need WinMain in order to not be a console app. This function is
// unused if we are a console application.
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
{
// Do the real work.
return main( __argc, __argv );
}
#endif

Просмотреть файл

@ -59,6 +59,8 @@ interface nsICommandLineRunner : nsICommandLine
* create a command line using other data. argv will not be altered in any
* way.
*
* On Windows, the "native" character set is UTF-8, not the native codepage.
*
* @param workingDir The working directory for resolving file and URI paths.
* @param state The nsICommandLine.state flag.
*/

Просмотреть файл

@ -384,20 +384,17 @@ nsCommandLine::ResolveFile(const nsAString& aArgument, nsIFile* *aResult)
// going to fail, and I haven't figured out a way to work around this without
// the PathCombine() function, which is not available in plain win95/nt4
nsCAutoString fullPath;
mWorkingDir->GetNativePath(fullPath);
nsCAutoString carg;
NS_CopyUnicodeToNative(aArgument, carg);
nsAutoString fullPath;
mWorkingDir->GetPath(fullPath);
fullPath.Append('\\');
fullPath.Append(carg);
fullPath.Append(aArgument);
char pathBuf[MAX_PATH];
if (!_fullpath(pathBuf, fullPath.get(), MAX_PATH))
WCHAR pathBuf[MAX_PATH];
if (!_wfullpath(pathBuf, fullPath.get(), MAX_PATH))
return NS_ERROR_FAILURE;
rv = lf->InitWithNativePath(nsDependentCString(pathBuf));
rv = lf->InitWithPath(nsDependentString(pathBuf));
if (NS_FAILED(rv)) return rv;
}
NS_ADDREF(*aResult = lf);
@ -471,7 +468,11 @@ nsCommandLine::appendArg(const char* arg)
#endif
nsAutoString warg;
#ifdef XP_WIN
CopyUTF8toUTF16(nsDependentCString(arg), warg);
#else
NS_CopyNativeToUnicode(nsDependentCString(arg), warg);
#endif
mArgs.AppendString(warg);
}

Просмотреть файл

@ -52,7 +52,7 @@ CPPSRCS = \
PROGRAM = updater$(BIN_SUFFIX)
REQUIRES = libmar libbz2
REQUIRES = libmar libbz2 string
LIBS += \
$(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) \
@ -65,6 +65,7 @@ HAVE_PROGRESSUI = 1
RCINCLUDE = updater.rc
CPPSRCS += progressui_win.cpp
OS_LIBS += $(call EXPAND_LIBNAME,comctl32 ws2_32 shell32)
DEFINES += -DUNICODE -D_UNICODE
ifndef GNU_CC
RCFLAGS += -I$(srcdir)
else
@ -104,6 +105,12 @@ endif
include $(topsrcdir)/config/rules.mk
DEFINES += -DNS_NO_XPCOM
ifdef _MSC_VER
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
endif
ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
libs::
$(NSINSTALL) -D $(DIST)/bin/updater.app

Просмотреть файл

@ -95,6 +95,16 @@ ArchiveReader::ExtractFile(const char *name, const char *dest)
return rv;
}
int
ArchiveReader::ExtractFileToStream(const char *name, FILE *fp)
{
const MarItem *item = mar_find_item(mArchive, name);
if (!item)
return READ_ERROR;
return ExtractItemToStream(item, fp);
}
int
ArchiveReader::ExtractItemToStream(const MarItem *item, FILE *fp)
{

Просмотреть файл

@ -53,6 +53,7 @@ public:
void Close();
int ExtractFile(const char *item, const char *destination);
int ExtractFileToStream(const char *item, FILE *fp);
private:
int ExtractItemToStream(const MarItem *item, FILE *fp);

Просмотреть файл

@ -39,8 +39,16 @@
#ifndef PROGRESSUI_H__
#define PROGRESSUI_H__
#if defined(XP_WIN)
typedef WCHAR NS_tchar;
#define NS_main wmain
#else
typedef char NS_tchar;
#define NS_main main
#endif
// Called to perform any initialization of the widget toolkit
int InitProgressUI(int *argc, char ***argv);
int InitProgressUI(int *argc, NS_tchar ***argv);
// Called on the main thread at startup
int ShowProgressUI();

Просмотреть файл

@ -43,6 +43,7 @@
#include <process.h>
#include <io.h>
#include "resource.h"
#include "progressui.h"
#define TIMER_ID 1
#define TIMER_INTERVAL 100
@ -72,16 +73,16 @@ static BOOL sQuit = FALSE;
static HFONT sSystemFont = 0;
static BOOL
GetStringsFile(char filename[MAX_PATH])
GetStringsFile(WCHAR filename[MAX_PATH])
{
if (!GetModuleFileName(NULL, filename, MAX_PATH))
if (!GetModuleFileNameW(NULL, filename, MAX_PATH))
return FALSE;
WCHAR *dot = wcsrchr(filename, '.');
if (!dot || wcsicmp(dot + 1, L"exe"))
return FALSE;
char *dot = strrchr(filename, '.');
if (!dot || stricmp(dot + 1, "exe"))
return FALSE;
strcpy(dot + 1, "ini");
wcscpy(dot + 1, L"ini");
return TRUE;
}
@ -95,7 +96,7 @@ UpdateDialog(HWND hDlg)
static void
ResizeDialogToFit(HWND hDlg)
{
char text[MAX_INFO_LENGTH];
WCHAR text[MAX_INFO_LENGTH];
RECT infoSize, textSize;
HFONT hInfoFont, hOldFont;
@ -173,23 +174,23 @@ CenterDialog(HWND hDlg)
}
static void
SetItemText(HWND hwnd, const char *key, const char *ini)
SetItemText(HWND hwnd, const WCHAR *key, const WCHAR *ini)
{
char text[MAX_INFO_LENGTH];
if (!GetPrivateProfileString("Strings", key, NULL, text, sizeof(text), ini))
WCHAR text[MAX_INFO_LENGTH];
if (!GetPrivateProfileStringW(L"Strings", key, NULL, text, sizeof(text), ini))
return;
SetWindowText(hwnd, text);
SetWindowTextW(hwnd, text);
}
static void
InitDialog(HWND hDlg)
{
char filename[MAX_PATH];
WCHAR filename[MAX_PATH];
if (!GetStringsFile(filename))
return;
SetItemText(hDlg, "Title", filename);
SetItemText(GetDlgItem(hDlg, IDC_INFO), "Info", filename);
SetItemText(hDlg, L"Title", filename);
SetItemText(GetDlgItem(hDlg, IDC_INFO), L"Info", filename);
// On Win9x, we need to send WM_SETFONT for l10n builds. Yes, we shouldn't
// use the system font. For example, if the text has Japanese characters on
@ -250,7 +251,7 @@ DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
}
int
InitProgressUI(int *argc, char ***argv)
InitProgressUI(int *argc, NS_tchar ***argv)
{
return 0;
}
@ -267,10 +268,10 @@ ShowProgressUI()
return 0;
// If we do not have updater.ini, then we should not bother showing UI.
char filename[MAX_PATH];
WCHAR filename[MAX_PATH];
if (!GetStringsFile(filename))
return -1;
if (_access(filename, 04))
if (_waccess(filename, 04))
return -1;
INITCOMMONCONTROLSEX icc = {

Просмотреть файл

@ -47,6 +47,41 @@
* LWS = 1*( " " | "\t" )
*/
#if defined(XP_WIN)
# include <windows.h>
# include <direct.h>
# include <io.h>
# define F_OK 00
# define W_OK 02
# define R_OK 04
# define access _access
# define putenv _putenv
# define snprintf _snprintf
# define fchmod(a,b)
# define mkdir(path, perms) _mkdir(path)
# define NS_T(str) L ## str
# define NS_tsnprintf _snwprintf
# define NS_tstrrchr wcsrchr
# define NS_tchdir _wchdir
# define NS_tremove _wremove
# define NS_topen _wopen
# define NS_tfopen _wfopen
# define NS_tatoi _wtoi64
#else
# include <sys/wait.h>
# include <unistd.h>
# define NS_T(str) str
# define NS_tsnprintf snprintf
# define NS_tstrrchr strrchr
# define NS_tchdir chdir
# define NS_tremove remove
# define NS_topen open
# define NS_tfopen fopen
# define NS_tatoi atoi
#endif
#include "bspatch.h"
#include "progressui.h"
#include "archivereader.h"
@ -64,24 +99,6 @@
#include <limits.h>
#include <errno.h>
#if defined(XP_WIN)
# include <windows.h>
# include <direct.h>
# include <io.h>
# define F_OK 00
# define W_OK 02
# define R_OK 04
# define access _access
# define snprintf _snprintf
# define putenv _putenv
# define fchmod(a,b)
# define mkdir(path, perm) _mkdir(path)
# define chdir(path) _chdir(path)
#else
# include <sys/wait.h>
# include <unistd.h>
#endif
#if defined(XP_MACOSX)
// This function is defined in launchchild_osx.mm
void LaunchChild(int argc, char **argv);
@ -261,7 +278,7 @@ private:
//-----------------------------------------------------------------------------
static char* gSourcePath;
static NS_tchar* gSourcePath;
static ArchiveReader gArchiveReader;
#ifdef XP_WIN
static bool gSucceeded = FALSE;
@ -281,10 +298,10 @@ static void LogInit()
if (gLogFP)
return;
char logFile[MAXPATHLEN];
snprintf(logFile, MAXPATHLEN, "%s/update.log", gSourcePath);
NS_tchar logFile[MAXPATHLEN];
NS_tsnprintf(logFile, MAXPATHLEN, NS_T("%s/update.log"), gSourcePath);
gLogFP = fopen(logFile, "w");
gLogFP = NS_tfopen(logFile, NS_T("w"));
}
static void LogFinish()
@ -359,7 +376,7 @@ mstrtok(const char *delims, char **str)
static void ensure_write_permissions(const char *path)
{
#ifdef XP_WIN
(void)_chmod(path, _S_IREAD | _S_IWRITE);
(void) chmod(path, _S_IREAD | _S_IWRITE);
#else
struct stat fs;
if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) {
@ -741,9 +758,11 @@ PatchFile::~PatchFile()
close(pfd);
// delete the temporary patch file
char spath[MAXPATHLEN];
snprintf(spath, MAXPATHLEN, "%s/%d.patch", gSourcePath, mPatchIndex);
ensure_remove(spath);
NS_tchar spath[MAXPATHLEN];
NS_tsnprintf(spath, MAXPATHLEN, NS_T("%s/%d.patch"),
gSourcePath, mPatchIndex);
NS_tremove(spath);
free(buf);
}
@ -818,12 +837,18 @@ PatchFile::Prepare()
// extract the patch to a temporary file
mPatchIndex = sPatchIndex++;
char spath[MAXPATHLEN];
snprintf(spath, MAXPATHLEN, "%s/%d.patch", gSourcePath, mPatchIndex);
NS_tchar spath[MAXPATHLEN];
NS_tsnprintf(spath, MAXPATHLEN, NS_T("%s/%d.patch"),
gSourcePath, mPatchIndex);
ensure_remove(spath);
NS_tremove(spath);
int rv = gArchiveReader.ExtractFile(mPatchFile, spath);
FILE *fp = NS_tfopen(spath, NS_T("wb"));
if (!fp)
return WRITE_ERROR;
int rv = gArchiveReader.ExtractFileToStream(mPatchFile, fp);
fclose(fp);
if (rv)
return rv;
@ -831,7 +856,7 @@ PatchFile::Prepare()
// no need to open all of the patch files and read all of
// the source files before applying any patches.
pfd = open(spath, O_RDONLY | _O_BINARY);
pfd = NS_topen(spath, O_RDONLY | _O_BINARY);
if (pfd < 0)
return READ_ERROR;
@ -1011,55 +1036,66 @@ PatchIfFile::Finish(int status)
#include "nsWindowsRestart.cpp"
static void
LaunchWinPostProcess(const char *appExe)
copyASCIItoWCHAR(WCHAR *dest, const char *src)
{
while (*src) {
*dest = *src;
++src; ++dest;
}
}
static void
LaunchWinPostProcess(const WCHAR *appExe)
{
// Launch helper.exe to perform post processing (e.g. registry and log file
// modifications) for the update.
char inifile[MAXPATHLEN];
strcpy(inifile, appExe);
WCHAR inifile[MAXPATHLEN];
wcscpy(inifile, appExe);
char *slash = strrchr(inifile, '\\');
WCHAR *slash = wcsrchr(inifile, '\\');
if (!slash)
return;
strcpy(slash + 1, "updater.ini");
wcscpy(slash + 1, L"updater.ini");
char exefile[MAXPATHLEN];
char exearg[MAXPATHLEN];
if (!GetPrivateProfileString("PostUpdateWin", "ExeRelPath", NULL, exefile,
sizeof(exefile), inifile))
WCHAR exefile[MAXPATHLEN];
WCHAR exearg[MAXPATHLEN];
if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", NULL, exefile,
MAXPATHLEN, inifile))
return;
if (!GetPrivateProfileString("PostUpdateWin", "ExeArg", NULL, exearg,
sizeof(exearg), inifile))
if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", NULL, exearg,
MAXPATHLEN, inifile))
return;
char exefullpath[MAXPATHLEN];
strcpy(exefullpath, appExe);
WCHAR exefullpath[MAXPATHLEN];
wcscpy(exefullpath, appExe);
slash = strrchr(exefullpath, '\\');
strcpy(slash + 1, exefile);
slash = wcsrchr(exefullpath, '\\');
wcscpy(slash + 1, exefile);
char dlogFile[MAXPATHLEN];
strcpy(dlogFile, exefullpath);
WCHAR dlogFile[MAXPATHLEN];
wcscpy(dlogFile, exefullpath);
slash = strrchr(dlogFile, '\\');
strcpy(slash + 1, "uninstall.update");
slash = wcsrchr(dlogFile, '\\');
wcscpy(slash + 1, L"uninstall.update");
char slogFile[MAXPATHLEN];
snprintf(slogFile, MAXPATHLEN, "%s/update.log", gSourcePath);
WCHAR slogFile[MAXPATHLEN];
_snwprintf(slogFile, MAXPATHLEN, L"%s/update.log", gSourcePath);
// We want to launch the post update helper app to update the Windows
// registry even if there is a failure with removing the uninstall.update
// file or copying the update.log file.
ensure_remove(dlogFile);
copy_file(slogFile, dlogFile);
NS_tremove(dlogFile);
CopyFile(slogFile, dlogFile, FALSE);
static int argc = 2;
static char **argv = (char**) malloc(sizeof(char*) * (argc + 1));
argv[0] = "argv0ignoredbywinlaunchchild";
argv[1] = exearg;
argv[2] = "\0";
static WCHAR* argv[3] = {
L"argv0ignoredbywinlaunchchild",
exearg,
L"\0"
};
WinLaunchChild(exefullpath, argc, argv, 1);
free(argv);
@ -1067,13 +1103,13 @@ LaunchWinPostProcess(const char *appExe)
#endif
static void
LaunchCallbackApp(const char *workingDir, int argc, char **argv)
LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv)
{
putenv("NO_EM_RESTART=");
putenv("MOZ_LAUNCHED_CHILD=1");
// Run from the specified working directory (see bug 312360).
chdir(workingDir);
NS_tchdir(workingDir);
#if defined(USE_EXECV)
execv(argv[0], argv);
@ -1134,7 +1170,7 @@ UpdateThreadFunc(void *param)
QuitProgressUI();
}
int main(int argc, char **argv)
int NS_main(int argc, NS_tchar **argv)
{
InitProgressUI(&argc, &argv);
@ -1150,7 +1186,7 @@ int main(int argc, char **argv)
return 1;
}
int pid = atoi(argv[2]);
int pid = NS_tatoi(argv[2]);
if (pid) {
#ifdef XP_WIN
HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, (DWORD) pid);
@ -1306,15 +1342,20 @@ ActionList::Finish(int status)
int DoUpdate()
{
char manifest[MAXPATHLEN];
snprintf(manifest, MAXPATHLEN, "%s/update.manifest", gSourcePath);
NS_tchar manifest[MAXPATHLEN];
NS_tsnprintf(manifest, MAXPATHLEN, NS_T("%s/update.manifest"), gSourcePath);
// extract the manifest
int rv = gArchiveReader.ExtractFile("update.manifest", manifest);
FILE *fp = NS_tfopen(manifest, NS_T("wb"));
if (!fp)
return READ_ERROR;
int rv = gArchiveReader.ExtractFileToStream("update.manifest", fp);
fclose(fp);
if (rv)
return rv;
AutoFD mfd = open(manifest, O_RDONLY | _O_BINARY);
AutoFD mfd = NS_topen(manifest, O_RDONLY | _O_BINARY);
if (mfd < 0)
return READ_ERROR;
@ -1394,13 +1435,3 @@ int DoUpdate()
list.Finish(rv);
return rv;
}
#if defined(XP_WIN) && !defined(DEBUG) && !defined(__GNUC__)
// We need WinMain in order to not be a console app. This function is unused
// if we are a console application.
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
{
// Do the real work.
return main(__argc, __argv);
}
#endif

Просмотреть файл

@ -929,13 +929,13 @@ XRE_GetFileFromPath(const char *aPath, nsILocalFile* *aResult)
aResult);
#elif defined(XP_WIN)
char fullPath[MAXPATHLEN];
WCHAR fullPath[MAXPATHLEN];
if (!_fullpath(fullPath, aPath, MAXPATHLEN))
if (!_wfullpath(fullPath, NS_ConvertUTF8toUTF16(aPath).get(), MAXPATHLEN))
return NS_ERROR_FAILURE;
return NS_NewNativeLocalFile(nsDependentCString(fullPath), PR_TRUE,
aResult);
return NS_NewLocalFile(nsDependentString(fullPath), PR_TRUE,
aResult);
#elif defined(XP_BEOS)
BPath fullPath;

Просмотреть файл

@ -291,9 +291,46 @@ SaveWordToEnv(const char *name, const nsACString & word)
static void
SaveFileToEnv(const char *name, nsIFile *file)
{
#ifdef XP_WIN
nsAutoString path;
file->GetPath(path);
SetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path.get());
#else
nsCAutoString path;
file->GetNativePath(path);
SaveWordToEnv(name, path);
#endif
}
// Load the path of a file saved with SaveFileToEnv
static already_AddRefed<nsILocalFile>
GetFileFromEnv(const char *name)
{
nsresult rv;
nsILocalFile *file = nsnull;
#ifdef XP_WIN
WCHAR path[_MAX_PATH];
if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(),
path, _MAX_PATH))
return nsnull;
rv = NS_NewLocalFile(nsDependentString(path), PR_TRUE, &file);
if (NS_FAILED(rv))
return nsnull;
return file;
#else
const char *arg = PR_GetEnv(name);
if (!arg || !*arg)
return nsnull;
rv = NS_NewNativeLocalFile(nsDependentCString(arg), PR_TRUE, &file);
if (NS_FAILED(rv))
return nsnull;
return file;
#endif
}
// Save the path of the given word to the specified environment variable
@ -691,8 +728,8 @@ nsXULAppInfo::LaunchAppHelperWithArgs(int aArgc, char **aArgv)
rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString appHelperPath;
rv = appHelper->GetNativePath(appHelperPath);
nsAutoString appHelperPath;
rv = appHelper->GetPath(appHelperPath);
NS_ENSURE_SUCCESS(rv, rv);
if (!WinLaunchChild(appHelperPath.get(), aArgc, aArgv, 1))
@ -1325,13 +1362,13 @@ XRE_GetBinaryPath(const char* argv0, nsILocalFile* *aResult)
// nsGREDirServiceProvider.cpp
#ifdef XP_WIN
char exePath[MAXPATHLEN];
PRUnichar exePath[MAXPATHLEN];
if (!::GetModuleFileName(0, exePath, MAXPATHLEN))
if (!::GetModuleFileNameW(0, exePath, MAXPATHLEN))
return NS_ERROR_FAILURE;
rv = NS_NewNativeLocalFile(nsDependentCString(exePath), PR_TRUE,
getter_AddRefs(lf));
rv = NS_NewLocalFile(nsDependentString(exePath), PR_TRUE,
getter_AddRefs(lf));
if (NS_FAILED(rv))
return rv;
@ -1579,15 +1616,22 @@ static nsresult LaunchChild(nsINativeAppSupport* aNative,
if (NS_FAILED(rv))
return rv;
#if defined(XP_WIN)
nsAutoString exePath;
rv = lf->GetPath(exePath);
if (NS_FAILED(rv))
return rv;
if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv, needElevation))
return NS_ERROR_FAILURE;
#else
nsCAutoString exePath;
rv = lf->GetNativePath(exePath);
if (NS_FAILED(rv))
return rv;
#if defined(XP_WIN)
if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv, needElevation))
return NS_ERROR_FAILURE;
#elif defined(XP_OS2) && (__GNUC__ == 3 && __GNUC_MINOR__ == 3)
#if defined(XP_OS2) && (__GNUC__ == 3 && __GNUC_MINOR__ == 3)
// implementation of _execv() is broken with GCC 3.3.x on OS/2
if (OS2LaunchChild(exePath.get(), gRestartArgc, gRestartArgv) == -1)
return NS_ERROR_FAILURE;
@ -1612,8 +1656,9 @@ static nsresult LaunchChild(nsINativeAppSupport* aNative,
PRStatus failed = PR_WaitProcess(process, &exitCode);
if (failed || exitCode)
return NS_ERROR_FAILURE;
#endif
#endif
#endif // XP_OS2 series
#endif // WP_WIN
#endif // WP_MACOSX
return NS_ERROR_LAUNCHED_CHILD_PROCESS;
}
@ -1855,20 +1900,11 @@ SelectProfile(nsIProfileLock* *aResult, nsINativeAppSupport* aNative,
*aStartOffline = PR_TRUE;
arg = PR_GetEnv("XRE_PROFILE_PATH");
if (arg && *arg) {
nsCOMPtr<nsILocalFile> lf;
rv = NS_NewNativeLocalFile(nsDependentCString(arg), PR_TRUE,
getter_AddRefs(lf));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILocalFile> localDir;
arg = PR_GetEnv("XRE_PROFILE_LOCAL_PATH");
if (arg && *arg) {
rv = NS_NewNativeLocalFile(nsDependentCString(arg), PR_TRUE,
getter_AddRefs(localDir));
NS_ENSURE_SUCCESS(rv, rv);
} else {
nsCOMPtr<nsILocalFile> lf = GetFileFromEnv("XRE_PROFILE_PATH");
if (lf) {
nsCOMPtr<nsILocalFile> localDir =
GetFileFromEnv("XRE_PROFILE_LOCAL_PATH");
if (!localDir) {
localDir = lf;
}
@ -1876,7 +1912,6 @@ SelectProfile(nsIProfileLock* *aResult, nsINativeAppSupport* aNative,
if (arg && *arg && aProfileName)
aProfileName->Assign(nsDependentCString(arg));
// Clear out flags that we handled (or should have handled!) last startup.
const char *dummy;
CheckArg("p", PR_FALSE, &dummy);

Просмотреть файл

@ -133,7 +133,7 @@ WriteConsoleLog();
#ifdef XP_WIN
BOOL
WinLaunchChild(const char *exePath, int argc, char **argv, int needElevation);
WinLaunchChild(const PRUnichar *exePath, int argc, char **argv, int needElevation);
#endif
#define NS_NATIVEAPPSUPPORT_CONTRACTID "@mozilla.org/toolkit/native-app-support;1"

Просмотреть файл

@ -563,29 +563,29 @@ struct MessageWindow {
return retval;
}
// SendRequest: Pass string via WM_COPYDATA to message window.
NS_IMETHOD SendRequest( const char *cmd ) {
// Construct a data buffer <commandline>\0<workingdir>\0
int cmdlen = strlen(cmd);
char* cmdbuf = (char*) malloc(cmdlen + MAX_PATH + 1);
if (!cmdbuf)
return NS_ERROR_OUT_OF_MEMORY;
// SendRequest: Pass the command line via WM_COPYDATA to message window.
NS_IMETHOD SendRequest() {
WCHAR *cmd = ::GetCommandLineW();
WCHAR cwd[MAX_PATH];
_wgetcwd(cwd, MAX_PATH);
strcpy(cmdbuf, cmd);
_getcwd(cmdbuf + cmdlen + 1, MAX_PATH);
// Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
NS_ConvertUTF16toUTF8 utf8buffer(cmd);
utf8buffer.Append('\0');
AppendUTF16toUTF8(cwd, utf8buffer);
utf8buffer.Append('\0');
// We used to set dwData to zero, when we didn't send the working dir.
// Now we're using it as a version number.
COPYDATASTRUCT cds = {
1,
cmdlen + strlen(cmdbuf + cmdlen + 1) + 2,
(void*) cmdbuf
utf8buffer.Length(),
(void*) utf8buffer.get()
};
// Bring the already running Mozilla process to the foreground.
// nsWindow will restore the window (if minimized) and raise it.
::SetForegroundWindow( mHandle );
::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
free (cmdbuf);
return NS_OK;
}
@ -615,9 +615,9 @@ struct MessageWindow {
printf( "Working dir: %s\n", wdpath);
#endif
NS_NewNativeLocalFile(nsDependentCString(wdpath),
PR_FALSE,
getter_AddRefs(workingDir));
NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath),
PR_FALSE,
getter_AddRefs(workingDir));
}
(void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
@ -701,8 +701,7 @@ nsNativeAppSupportWin::Start( PRBool *aResult ) {
MessageWindow msgWindow;
if ( (HWND)msgWindow ) {
// We are a client process. Pass request to message window.
LPTSTR cmd = ::GetCommandLine();
rv = msgWindow.SendRequest( cmd );
rv = msgWindow.SendRequest();
} else {
// We will be server.
rv = msgWindow.Create();

Просмотреть файл

@ -363,6 +363,11 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
if (NS_FAILED(rv))
return;
nsAutoString updaterPathW;
rv = updater->GetPath(updaterPathW);
if (NS_FAILED(rv))
return;
// Get the directory to which the update will be applied. On Mac OSX we need
// to apply the update to the Foo.app directory which is the parent of the
// parent of the appDir. On other platforms we will just apply to the appDir.
@ -435,7 +440,7 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
#elif defined(XP_WIN)
_chdir(applyToDir.get());
if (!WinLaunchChild(updaterPath.get(), appArgc + 4, argv, 1))
if (!WinLaunchChild(updaterPathW.get(), appArgc + 4, argv, 1))
return;
_exit(0);
#else

Просмотреть файл

@ -44,6 +44,8 @@
#define nsWindowsRestart_cpp
#endif
#include "nsUTF8Utils.h"
#include <shellapi.h>
#ifndef ERROR_ELEVATION_REQUIRED
@ -65,7 +67,7 @@ BOOL (WINAPI *pIsUserAnAdmin)(VOID);
/**
* Get the length that the string will take when it is quoted.
*/
static int QuotedStrLen(const char *s)
static int QuotedStrLen(const PRUnichar *s)
{
int i = 2; // initial and final quote
while (*s) {
@ -73,7 +75,7 @@ static int QuotedStrLen(const char *s)
++i;
}
++i; ++s;
++i, ++s;
}
return i;
}
@ -85,7 +87,7 @@ static int QuotedStrLen(const char *s)
*
* @return the end of the string
*/
static char* QuoteString(char *d, const char *s)
static PRUnichar* QuoteString(PRUnichar *d, const PRUnichar *s)
{
*d = '"';
++d;
@ -109,9 +111,11 @@ static char* QuoteString(char *d, const char *s)
/**
* Create a quoted command from a list of arguments. The returned string
* is allocated with "malloc" and should be "free"d.
*
* argv is UTF8
*/
static char*
MakeCommandLine(int argc, char **argv)
static PRUnichar*
MakeCommandLine(int argc, PRUnichar **argv)
{
int i;
int len = 1; // null-termination
@ -119,11 +123,11 @@ MakeCommandLine(int argc, char **argv)
for (i = 0; i < argc; ++i)
len += QuotedStrLen(argv[i]) + 1;
char *s = (char*) malloc(len);
PRUnichar *s = (PRUnichar*) malloc(len * sizeof(PRUnichar));
if (!s)
return NULL;
char *c = s;
PRUnichar *c = s;
for (i = 0; i < argc; ++i) {
c = QuoteString(c, argv[i]);
*c = ' ';
@ -135,37 +139,20 @@ MakeCommandLine(int argc, char **argv)
return s;
}
/**
* alloc and convert multibyte char to unicode char
*/
static PRUnichar *
AllocConvertAToW(const char *buf)
{
PRUint32 inputLen = strlen(buf) + 1;
int n = MultiByteToWideChar(CP_ACP, 0, buf, inputLen, NULL, 0);
if (n <= 0)
return NULL;
PRUnichar *result = (PRUnichar *)malloc(n * sizeof(PRUnichar));
if (!result)
return NULL;
MultiByteToWideChar(CP_ACP, 0, buf, inputLen, result, n);
return result;
}
/**
* Launch a child process without elevated privilege.
*/
static BOOL
LaunchAsNormalUser(const char *exePath, char *cl)
LaunchAsNormalUser(const PRUnichar *exePath, PRUnichar *cl)
{
if (!pCreateProcessWithTokenW) {
// IsUserAnAdmin is not present on Win9x and not exported by name on Win2k
*(FARPROC *)&pIsUserAnAdmin =
GetProcAddress(GetModuleHandle("shell32.dll"), "IsUserAnAdmin");
GetProcAddress(GetModuleHandleA("shell32.dll"), "IsUserAnAdmin");
// CreateProcessWithTokenW is not present on WinXP or earlier
*(FARPROC *)&pCreateProcessWithTokenW =
GetProcAddress(GetModuleHandle("advapi32.dll"),
GetProcAddress(GetModuleHandleA("advapi32.dll"),
"CreateProcessWithTokenW");
if (!pCreateProcessWithTokenW)
@ -177,7 +164,7 @@ LaunchAsNormalUser(const char *exePath, char *cl)
return FALSE;
// borrow the shell token to drop the privilege
HWND hwndShell = FindWindow("Progman", NULL);
HWND hwndShell = FindWindowA("Progman", NULL);
DWORD dwProcessId;
GetWindowThreadProcessId(hwndShell, &dwProcessId);
@ -205,22 +192,24 @@ LaunchAsNormalUser(const char *exePath, char *cl)
STARTUPINFOW si = {sizeof(si), 0};
PROCESS_INFORMATION pi = {0};
PRUnichar *exePathW = AllocConvertAToW(exePath);
PRUnichar *clW = AllocConvertAToW(cl);
ok = exePathW && clW;
if (ok) {
ok = pCreateProcessWithTokenW(hNewToken,
0, // profile is already loaded
exePathW,
clW,
0, // No special process creation flags
NULL, // inherit my environment
NULL, // use my current directory
&si,
&pi);
}
free(exePathW);
free(clW);
// When launching with reduced privileges, environment inheritance
// (passing NULL as lpEnvironment) doesn't work correctly. Pass our
// current environment block explicitly
WCHAR* myenv = GetEnvironmentStringsW();
ok = pCreateProcessWithTokenW(hNewToken,
0, // profile is already loaded
exePath,
cl,
CREATE_UNICODE_ENVIRONMENT,
myenv, // inherit my environment
NULL, // use my current directory
&si,
&pi);
if (myenv)
FreeEnvironmentStringsW(myenv);
CloseHandle(hNewToken);
if (!ok)
return FALSE;
@ -231,26 +220,80 @@ LaunchAsNormalUser(const char *exePath, char *cl)
return TRUE;
}
/**
* Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
* can't link to updater.exe.
*/
static PRUnichar*
AllocConvertUTF8toUTF16(const char *arg)
{
// UTF16 can't be longer in units than UTF8
int len = strlen(arg);
PRUnichar *s = new PRUnichar[(len + 1) * sizeof(PRUnichar)];
if (!s)
return NULL;
ConvertUTF8toUTF16 convert(s);
len = convert.write(arg, len);
s[len] = '\0';
return s;
}
static void
FreeAllocStrings(int argc, PRUnichar **argv)
{
while (argc) {
--argc;
delete [] argv[argc];
}
delete [] argv;
}
/**
* Launch a child process with the specified arguments.
* @param needElevation 1:need elevation, -1:want to drop priv, 0:don't care
* @note argv[0] is ignored
* @note The form of this function that takes char **argv expects UTF-8
*/
BOOL
WinLaunchChild(const char *exePath, int argc, char **argv, int needElevation)
WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv, int needElevation);
BOOL
WinLaunchChild(const PRUnichar *exePath, int argc, char **argv, int needElevation)
{
char *cl;
PRUnichar** argvConverted = new PRUnichar*[argc];
if (!argvConverted)
return FALSE;
for (int i = 0; i < argc; ++i) {
argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]);
if (!argvConverted[i]) {
return FALSE;
}
}
BOOL ok = WinLaunchChild(exePath, argc, argvConverted, needElevation);
FreeAllocStrings(argc, argvConverted);
return ok;
}
BOOL
WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv, int needElevation)
{
PRUnichar *cl;
BOOL ok;
if (needElevation > 0) {
cl = MakeCommandLine(argc - 1, argv + 1);
if (!cl)
return FALSE;
ok = ShellExecute(NULL, // no special UI window
NULL, // use default verb
exePath,
cl,
NULL, // use my current directory
SW_SHOWDEFAULT) > (HINSTANCE)32;
ok = ShellExecuteW(NULL, // no special UI window
NULL, // use default verb
exePath,
cl,
NULL, // use my current directory
SW_SHOWDEFAULT) > (HINSTANCE)32;
free(cl);
return ok;
}
@ -267,19 +310,19 @@ WinLaunchChild(const char *exePath, int argc, char **argv, int needElevation)
needElevation = 0;
}
if (needElevation == 0) {
STARTUPINFO si = {sizeof(si), 0};
STARTUPINFOW si = {sizeof(si), 0};
PROCESS_INFORMATION pi = {0};
ok = CreateProcess(exePath,
cl,
NULL, // no special security attributes
NULL, // no special thread attributes
FALSE, // don't inherit filehandles
0, // No special process creation flags
NULL, // inherit my environment
NULL, // use my current directory
&si,
&pi);
ok = CreateProcessW(exePath,
cl,
NULL, // no special security attributes
NULL, // no special thread attributes
FALSE, // don't inherit filehandles
0, // No special process creation flags
NULL, // inherit my environment
NULL, // use my current directory
&si,
&pi);
if (ok) {
CloseHandle(pi.hProcess);

Просмотреть файл

@ -0,0 +1,58 @@
// This file is a .cpp file meant to be included in nsBrowserApp.cpp and other
// similar bootstrap code. It converts wide-character windows wmain into UTF-8
// narrow-character strings.
#ifndef XP_WIN
#error This file only makes sense on Windows.
#endif
#include "nsUTF8Utils.h"
#define main NS_internal_main
int main(int argc, char **argv);
static char*
AllocConvertUTF16toUTF8(const WCHAR *arg)
{
// be generous... UTF16 units can expand up to 3 UTF8 units
int len = wcslen(arg);
char *s = new char[len * 3 + 1];
if (!s)
return NULL;
ConvertUTF16toUTF8 convert(s);
len = convert.write(arg, len);
s[len] = '\0';
return s;
}
static void
FreeAllocStrings(int argc, char **argv)
{
while (argc) {
--argc;
delete [] argv[argc];
}
delete [] argv;
}
int wmain(int argc, WCHAR **argv)
{
char **argvConverted = new char*[argc + 1];
if (!argvConverted)
return 127;
for (int i = 0; i < argc; ++i) {
argvConverted[i] = AllocConvertUTF16toUTF8(argv[i]);
if (!argvConverted[i]) {
return 127;
}
}
argvConverted[argc] = NULL;
int result = main(argc, argvConverted);
FreeAllocStrings(argc, argvConverted);
return result;
}

Просмотреть файл

@ -254,8 +254,9 @@ struct nsXREAppData
* Begin an XUL application. Does not return until the user exits the
* application.
*
* @param argc/argv Command-line parameters to pass to the application. These
* are in the "native" character set.
* @param argc/argv Command-line parameters to pass to the application. On
* Windows, these should be in UTF8. On unix-like platforms
* these are in the "native" character set.
*
* @param aAppData Information about the application to be run.
*
@ -264,10 +265,6 @@ struct nsXREAppData
* @note If the binary is linked against the standalone XPCOM glue,
* XPCOMGlueStartup() should be called before this method.
*
* @note XXXbsmedberg Nobody uses the glue yet, but there is a
* potential problem: on windows, the standalone glue calls
* SetCurrentDirectory, and relative paths on the command line
* won't be correct.
*/
XRE_API(int,
XRE_main, (int argc, char* argv[], const nsXREAppData* sAppData))
@ -275,6 +272,8 @@ XRE_API(int,
/**
* Given a path relative to the current working directory (or an absolute
* path), return an appropriate nsILocalFile object.
*
* @note Pass UTF8 strings on Windows... native charset on other platforms.
*/
XRE_API(nsresult,
XRE_GetFileFromPath, (const char *aPath, nsILocalFile* *aResult))

Просмотреть файл

@ -54,6 +54,7 @@
#include "nsIWindowsRegKey.h"
#include "nsIProcess.h"
#include "nsOSHelperAppService.h"
#include "nsUnicharUtils.h"
NS_IMPL_ISUPPORTS_INHERITED1(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag)
@ -541,15 +542,15 @@ PRBool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName
// Helper routine used in tracking app lists. Converts path
// entries to lower case and stores them in the trackList array.
void nsMIMEInfoWin::ProcessPath(nsCOMPtr<nsIMutableArray>& appList,
nsTArray<nsCAutoString>& trackList,
nsTArray<nsString>& trackList,
const nsAString& appFilesystemCommand)
{
NS_ConvertUTF16toUTF8 lower(appFilesystemCommand);
nsAutoString lower(appFilesystemCommand);
ToLowerCase(lower);
// Don't include firefox.exe in the list
char exe[MAX_PATH+1];
PRUint32 len = GetModuleFileName(NULL, exe, MAX_PATH);
WCHAR exe[MAX_PATH+1];
PRUint32 len = GetModuleFileNameW(NULL, exe, MAX_PATH);
if (len < MAX_PATH && len != 0) {
PRUint32 index = lower.Find(exe);
if (index != -1)
@ -568,11 +569,11 @@ void nsMIMEInfoWin::ProcessPath(nsCOMPtr<nsIMutableArray>& appList,
// Helper routine that handles a compare between a path
// and an array of paths.
static PRBool IsPathInList(nsAString& appPath,
nsTArray<nsCAutoString>& trackList)
nsTArray<nsString>& trackList)
{
// trackList data is always lowercase, see ProcessPath
// above.
NS_ConvertUTF16toUTF8 tmp(appPath);
nsAutoString tmp(appPath);
ToLowerCase(tmp);
for (PRUint32 i = 0; i < trackList.Length(); i++) {
@ -604,7 +605,7 @@ nsMIMEInfoWin::GetPossibleLocalHandlers(nsIArray **_retval)
if (!appList)
return NS_ERROR_FAILURE;
nsTArray<nsCAutoString> trackList;
nsTArray<nsString> trackList;
nsCAutoString fileExt;
GetPrimaryExtension(fileExt);

Просмотреть файл

@ -94,7 +94,7 @@ class nsMIMEInfoWin : public nsMIMEInfoBase, public nsIPropertyBag {
// Helper routine used in tracking app lists
void ProcessPath(nsCOMPtr<nsIMutableArray>& appList,
nsTArray<nsCAutoString>& trackList,
nsTArray<nsString>& trackList,
const nsAString& appFilesystemCommand);
};

Просмотреть файл

@ -62,10 +62,20 @@
// for |PRUnichar|
#endif
// This file may be used (through nsUTF8Utils.h) from non-XPCOM code, in
// particular the standalone software updater. In that case stub out
// the macros provided by nsDebug.h which are only usable when linking XPCOM
#ifdef NS_NO_XPCOM
#define NS_WARNING(msg)
#define NS_ASSERTION(cond, msg)
#define NS_ERROR(msg)
#else
#ifndef nsDebug_h__
#include "nsDebug.h"
// for NS_ASSERTION
#endif
#endif
#ifdef HAVE_CPP_BOOL
typedef bool nsCharTraits_bool;

Просмотреть файл

@ -38,6 +38,12 @@
#ifndef nsUTF8Utils_h_
#define nsUTF8Utils_h_
// This file may be used in two ways: if MOZILLA_INTERNAL_API is defined, this
// file will provide signatures for the Mozilla abstract string types. It will
// use XPCOM assertion/debugging macros, etc.
#include "nscore.h"
#include "nsCharTraits.h"
class UTF8traits
@ -136,6 +142,8 @@ public:
return ucs4;
}
#ifdef MOZILLA_INTERNAL_API
static PRUint32 NextChar(nsACString::const_iterator& iter,
const nsACString::const_iterator& end,
PRBool *err = nsnull, PRBool *overlong = nsnull)
@ -199,6 +207,7 @@ public:
*overlong = ucs4 < minUcs4;
return ucs4;
}
#endif // MOZILLA_INTERNAL_API
private:
static PRBool CalcState(char c, PRUint32& ucs4, PRUint32& minUcs4,
@ -356,6 +365,8 @@ public:
return 0;
}
#ifdef MOZILLA_INTERNAL_API
static PRUint32 NextChar(nsAString::const_iterator& iter,
const nsAString::const_iterator& end,
PRBool *err = nsnull)
@ -437,6 +448,7 @@ public:
*err = PR_TRUE;
return 0;
}
#endif // MOZILLA_INTERNAL_API
};
@ -447,8 +459,8 @@ public:
class ConvertUTF8toUTF16
{
public:
typedef nsACString::char_type value_type;
typedef nsAString::char_type buffer_type;
typedef char value_type;
typedef PRUnichar buffer_type;
ConvertUTF8toUTF16( buffer_type* aBuffer )
: mStart(aBuffer), mBuffer(aBuffer), mErrorEncountered(PR_FALSE) {}
@ -533,7 +545,7 @@ class ConvertUTF8toUTF16
class CalculateUTF8Length
{
public:
typedef nsACString::char_type value_type;
typedef char value_type;
CalculateUTF8Length() : mLength(0), mErrorEncountered(PR_FALSE) { }
@ -602,8 +614,8 @@ class CalculateUTF8Length
class ConvertUTF16toUTF8
{
public:
typedef nsAString::char_type value_type;
typedef nsACString::char_type buffer_type;
typedef PRUnichar value_type;
typedef char buffer_type;
// The error handling here is more lenient than that in
// |ConvertUTF8toUTF16|, but it's that way for backwards
@ -716,7 +728,7 @@ class ConvertUTF16toUTF8
class CalculateUTF8Size
{
public:
typedef nsAString::char_type value_type;
typedef PRUnichar value_type;
CalculateUTF8Size()
: mSize(0) { }
@ -780,6 +792,7 @@ class CalculateUTF8Size
size_t mSize;
};
#ifdef MOZILLA_INTERNAL_API
/**
* A character sink that performs a |reinterpret_cast| style conversion
* between character types.
@ -816,5 +829,6 @@ class LossyConvertEncoding
private:
output_type* mDestination;
};
#endif // MOZILLA_INTERNAL_API
#endif /* !defined(nsUTF8Utils_h_) */

Просмотреть файл

@ -133,6 +133,12 @@ NSDISTMODE = copy
include $(topsrcdir)/config/config.mk
ifdef _MSC_VER
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
endif
ifdef NS_TRACE_MALLOC
EXTRA_DSO_LIBS += tracemalloc
endif

Просмотреть файл

@ -57,6 +57,10 @@
#include "prenv.h"
#include "nsINIParser.h"
#ifdef XP_WIN
#include "nsWindowsWMain.cpp"
#endif
/**
* Output a string to the user. This method is really only meant to be used to
* output last-ditch error messages designed for developers NOT END USERS.
@ -445,13 +449,3 @@ int main(int argc, char* argv[])
return XRE_main(argc, argv, appData);
}
#if defined( XP_WIN ) && defined( WIN32 ) && !defined(__GNUC__)
// We need WinMain in order to not be a console app. This function is
// unused if we are a console application.
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
{
// Do the real work.
return main( __argc, __argv );
}
#endif

Просмотреть файл

@ -44,7 +44,7 @@ include $(DEPTH)/config/autoconf.mk
MODULE = xulrunner
REQUIRES = xpcom xulapp
REQUIRES = xpcom string xulapp
CPPSRCS = nsXULStub.cpp
@ -84,6 +84,12 @@ MOZ_WINCONSOLE = 0
endif
endif
include $(topsrcdir)/config/config.mk
ifdef _MSC_VER
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
endif
include $(topsrcdir)/config/rules.mk
ifeq ($(OS_ARCH),WINNT)

Просмотреть файл

@ -51,7 +51,6 @@
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define PATH_SEPARATOR_CHAR '\\'
#include "nsWindowsRestart.cpp"
#define R_OK 04
#elif defined(XP_MACOSX)
#include <CFBundle.h>
@ -72,6 +71,10 @@
#define PATH_SEPARATOR_CHAR '/'
#endif
#ifdef XP_WIN
#include "nsWindowsWMain.cpp"
#endif
#define VERSION_MAXLEN 128
static void Output(PRBool isError, const char *fmt, ... )
@ -351,13 +354,3 @@ main(int argc, char **argv)
return retval;
}
#if defined( XP_WIN ) && defined( WIN32 ) && !defined(__GNUC__)
// We need WinMain in order to not be a console app. This function is
// unused if we are a console application.
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
{
// Do the real work.
return main( __argc, __argv );
}
#endif