michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsXPCOMGlue.h" michael@0: #include "nsINIParser.h" michael@0: #include "nsXPCOMPrivate.h" // for XP MAXPATHLEN michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsIFile.h" michael@0: michael@0: #include michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include michael@0: #define snprintf _snprintf michael@0: #define vsnprintf _vsnprintf michael@0: #define strcasecmp _stricmp michael@0: #define PATH_SEPARATOR_CHAR '\\' michael@0: #define R_OK 04 michael@0: #elif defined(XP_MACOSX) michael@0: #include michael@0: #include michael@0: #include michael@0: #define PATH_SEPARATOR_CHAR '/' michael@0: #else michael@0: #include michael@0: #include michael@0: #include michael@0: #define PATH_SEPARATOR_CHAR '/' michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: #define XRE_DONT_PROTECT_DLL_LOAD michael@0: #include "nsWindowsWMain.cpp" michael@0: #endif michael@0: michael@0: #define VERSION_MAXLEN 128 michael@0: michael@0: static void Output(bool isError, const char *fmt, ... ) michael@0: { michael@0: va_list ap; michael@0: va_start(ap, fmt); michael@0: michael@0: #if (defined(XP_WIN) && !MOZ_WINCONSOLE) michael@0: char msg[2048]; michael@0: michael@0: vsnprintf(msg, sizeof(msg), fmt, ap); michael@0: michael@0: UINT flags = MB_OK; michael@0: if (isError) michael@0: flags |= MB_ICONERROR; michael@0: else michael@0: flags |= MB_ICONINFORMATION; michael@0: michael@0: wchar_t wide_msg[1024]; michael@0: MultiByteToWideChar(CP_ACP, michael@0: 0, michael@0: msg, michael@0: -1, michael@0: wide_msg, michael@0: sizeof(wide_msg) / sizeof(wchar_t)); michael@0: michael@0: MessageBoxW(nullptr, wide_msg, L"XULRunner", flags); michael@0: #else michael@0: vfprintf(stderr, fmt, ap); michael@0: #endif michael@0: michael@0: va_end(ap); michael@0: } michael@0: michael@0: /** michael@0: * Return true if |arg| matches the given argument name. michael@0: */ michael@0: static bool IsArg(const char* arg, const char* s) michael@0: { michael@0: if (*arg == '-') michael@0: { michael@0: if (*++arg == '-') michael@0: ++arg; michael@0: return !strcasecmp(arg, s); michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: if (*arg == '/') michael@0: return !strcasecmp(++arg, s); michael@0: #endif michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Return true if |aDir| is a valid file/directory. michael@0: */ michael@0: static bool FolderExists(const char* aDir) michael@0: { michael@0: #ifdef XP_WIN michael@0: wchar_t wideDir[MAX_PATH]; michael@0: MultiByteToWideChar(CP_UTF8, 0, aDir, -1, wideDir, MAX_PATH); michael@0: DWORD fileAttrs = GetFileAttributesW(wideDir); michael@0: return fileAttrs != INVALID_FILE_ATTRIBUTES; michael@0: #else michael@0: return access(aDir, R_OK) == 0; michael@0: #endif michael@0: } michael@0: michael@0: static nsresult GetRealPath(const char* appDataFile, char* *aResult) michael@0: { michael@0: #ifdef XP_WIN michael@0: wchar_t wAppDataFile[MAX_PATH]; michael@0: wchar_t wIniPath[MAX_PATH]; michael@0: MultiByteToWideChar(CP_UTF8, 0, appDataFile, -1, wAppDataFile, MAX_PATH); michael@0: _wfullpath(wIniPath, wAppDataFile, MAX_PATH); michael@0: WideCharToMultiByte(CP_UTF8, 0, wIniPath, -1, *aResult, MAX_PATH, 0, 0); michael@0: #else michael@0: struct stat fileStat; michael@0: if (!realpath(appDataFile, *aResult) || stat(*aResult, &fileStat)) michael@0: return NS_ERROR_FAILURE; michael@0: #endif michael@0: if (!*aResult || !**aResult) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class AutoAppData michael@0: { michael@0: public: michael@0: AutoAppData(nsIFile* aINIFile) : mAppData(nullptr) { michael@0: nsresult rv = XRE_CreateAppData(aINIFile, &mAppData); michael@0: if (NS_FAILED(rv)) michael@0: mAppData = nullptr; michael@0: } michael@0: ~AutoAppData() { michael@0: if (mAppData) michael@0: XRE_FreeAppData(mAppData); michael@0: } michael@0: michael@0: operator nsXREAppData*() const { return mAppData; } michael@0: nsXREAppData* operator -> () const { return mAppData; } michael@0: michael@0: private: michael@0: nsXREAppData* mAppData; michael@0: }; michael@0: michael@0: XRE_CreateAppDataType XRE_CreateAppData; michael@0: XRE_FreeAppDataType XRE_FreeAppData; michael@0: XRE_mainType XRE_main; michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: nsresult rv; michael@0: char *lastSlash; michael@0: michael@0: char iniPath[MAXPATHLEN]; michael@0: char tmpPath[MAXPATHLEN]; michael@0: char greDir[MAXPATHLEN]; michael@0: bool greFound = false; michael@0: michael@0: #if defined(XP_MACOSX) michael@0: CFBundleRef appBundle = CFBundleGetMainBundle(); michael@0: if (!appBundle) michael@0: return 1; michael@0: michael@0: CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(appBundle); michael@0: if (!resourcesURL) michael@0: return 1; michael@0: michael@0: CFURLRef absResourcesURL = CFURLCopyAbsoluteURL(resourcesURL); michael@0: CFRelease(resourcesURL); michael@0: if (!absResourcesURL) michael@0: return 1; michael@0: michael@0: CFURLRef iniFileURL = michael@0: CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, michael@0: absResourcesURL, michael@0: CFSTR("application.ini"), michael@0: false); michael@0: CFRelease(absResourcesURL); michael@0: if (!iniFileURL) michael@0: return 1; michael@0: michael@0: CFStringRef iniPathStr = michael@0: CFURLCopyFileSystemPath(iniFileURL, kCFURLPOSIXPathStyle); michael@0: CFRelease(iniFileURL); michael@0: if (!iniPathStr) michael@0: return 1; michael@0: michael@0: CFStringGetCString(iniPathStr, iniPath, sizeof(iniPath), michael@0: kCFStringEncodingUTF8); michael@0: CFRelease(iniPathStr); michael@0: michael@0: #else michael@0: michael@0: #ifdef XP_WIN michael@0: wchar_t wide_path[MAX_PATH]; michael@0: if (!::GetModuleFileNameW(nullptr, wide_path, MAX_PATH)) michael@0: return 1; michael@0: michael@0: WideCharToMultiByte(CP_UTF8, 0, wide_path,-1, michael@0: iniPath, MAX_PATH, nullptr, nullptr); michael@0: michael@0: #else michael@0: // on unix, there is no official way to get the path of the current binary. michael@0: // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to michael@0: // multiple applications, we will try a series of techniques: michael@0: // michael@0: // 1) use realpath() on argv[0], which works unless we're loaded from the michael@0: // PATH michael@0: // 2) manually walk through the PATH and look for ourself michael@0: // 3) give up michael@0: michael@0: struct stat fileStat; michael@0: strncpy(tmpPath, argv[0], sizeof(tmpPath)); michael@0: lastSlash = strrchr(tmpPath, '/'); michael@0: if (lastSlash) { michael@0: *lastSlash = 0; michael@0: realpath(tmpPath, iniPath); michael@0: } else { michael@0: const char *path = getenv("PATH"); michael@0: if (!path) michael@0: return 1; michael@0: michael@0: char *pathdup = strdup(path); michael@0: if (!pathdup) michael@0: return 1; michael@0: michael@0: bool found = false; michael@0: char *token = strtok(pathdup, ":"); michael@0: while (token) { michael@0: sprintf(tmpPath, "%s/%s", token, argv[0]); michael@0: if (stat(tmpPath, &fileStat) == 0) { michael@0: found = true; michael@0: lastSlash = strrchr(tmpPath, '/'); michael@0: *lastSlash = 0; michael@0: realpath(tmpPath, iniPath); michael@0: break; michael@0: } michael@0: token = strtok(nullptr, ":"); michael@0: } michael@0: free (pathdup); michael@0: if (!found) michael@0: return 1; michael@0: } michael@0: lastSlash = iniPath + strlen(iniPath); michael@0: *lastSlash = '/'; michael@0: #endif michael@0: michael@0: #ifndef XP_UNIX michael@0: lastSlash = strrchr(iniPath, PATH_SEPARATOR_CHAR); michael@0: if (!lastSlash) michael@0: return 1; michael@0: #endif michael@0: michael@0: *(++lastSlash) = '\0'; michael@0: michael@0: // On Linux/Win, look for XULRunner in appdir/xulrunner michael@0: michael@0: snprintf(greDir, sizeof(greDir), michael@0: "%sxulrunner" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL, michael@0: iniPath); michael@0: michael@0: greFound = FolderExists(greDir); michael@0: michael@0: #ifdef XP_UNIX michael@0: if (greFound) { michael@0: char resolved_greDir[MAXPATHLEN] = ""; michael@0: if (realpath(greDir, resolved_greDir) && *resolved_greDir) { michael@0: strncpy(greDir, resolved_greDir, MAXPATHLEN); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: strncpy(lastSlash, "application.ini", sizeof(iniPath) - (lastSlash - iniPath)); michael@0: michael@0: #endif michael@0: michael@0: // If -app parameter was passed in, it is now time to take it under michael@0: // consideration. michael@0: const char *appDataFile; michael@0: appDataFile = getenv("XUL_APP_FILE"); michael@0: if (!appDataFile || !*appDataFile) michael@0: if (argc > 1 && IsArg(argv[1], "app")) { michael@0: if (argc == 2) { michael@0: Output(false, "specify APP-FILE (optional)\n"); michael@0: return 1; michael@0: } michael@0: argv[1] = argv[0]; michael@0: ++argv; michael@0: --argc; michael@0: michael@0: appDataFile = argv[1]; michael@0: argv[1] = argv[0]; michael@0: ++argv; michael@0: --argc; michael@0: michael@0: char kAppEnv[MAXPATHLEN]; michael@0: snprintf(kAppEnv, MAXPATHLEN, "XUL_APP_FILE=%s", appDataFile); michael@0: if (putenv(kAppEnv)) michael@0: Output(false, "Couldn't set %s.\n", kAppEnv); michael@0: michael@0: char *result = (char*) calloc(sizeof(char), MAXPATHLEN); michael@0: if (NS_FAILED(GetRealPath(appDataFile, &result))) { michael@0: Output(true, "Invalid application.ini path.\n"); michael@0: return 1; michael@0: } michael@0: michael@0: // We have a valid application.ini path passed in to the -app parameter michael@0: // but not yet a valid greDir, so lets look for it also on the same folder michael@0: // as the stub. michael@0: if (!greFound) { michael@0: lastSlash = strrchr(iniPath, PATH_SEPARATOR_CHAR); michael@0: if (!lastSlash) michael@0: return 1; michael@0: michael@0: *(++lastSlash) = '\0'; michael@0: michael@0: snprintf(greDir, sizeof(greDir), "%s" XPCOM_DLL, iniPath); michael@0: greFound = FolderExists(greDir); michael@0: } michael@0: michael@0: // copy it back. michael@0: strcpy(iniPath, result); michael@0: } michael@0: michael@0: nsINIParser parser; michael@0: rv = parser.Init(iniPath); michael@0: if (NS_FAILED(rv)) { michael@0: fprintf(stderr, "Could not read application.ini\n"); michael@0: return 1; michael@0: } michael@0: michael@0: if (!greFound) { michael@0: #ifdef XP_MACOSX michael@0: // Check for /Contents/Frameworks/XUL.framework/Versions/Current/libmozglue.dylib michael@0: CFURLRef fwurl = CFBundleCopyPrivateFrameworksURL(appBundle); michael@0: CFURLRef absfwurl = nullptr; michael@0: if (fwurl) { michael@0: absfwurl = CFURLCopyAbsoluteURL(fwurl); michael@0: CFRelease(fwurl); michael@0: } michael@0: michael@0: if (absfwurl) { michael@0: CFURLRef xulurl = michael@0: CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl, michael@0: CFSTR("XUL.framework/Versions/Current"), michael@0: true); michael@0: michael@0: if (xulurl) { michael@0: CFURLRef xpcomurl = michael@0: CFURLCreateCopyAppendingPathComponent(nullptr, xulurl, michael@0: CFSTR("libmozglue.dylib"), michael@0: false); michael@0: michael@0: if (xpcomurl) { michael@0: char tbuffer[MAXPATHLEN]; michael@0: michael@0: if (CFURLGetFileSystemRepresentation(xpcomurl, true, michael@0: (UInt8*) tbuffer, michael@0: sizeof(tbuffer)) && michael@0: access(tbuffer, R_OK | X_OK) == 0) { michael@0: if (realpath(tbuffer, greDir)) { michael@0: greFound = true; michael@0: } michael@0: else { michael@0: greDir[0] = '\0'; michael@0: } michael@0: } michael@0: michael@0: CFRelease(xpcomurl); michael@0: } michael@0: michael@0: CFRelease(xulurl); michael@0: } michael@0: michael@0: CFRelease(absfwurl); michael@0: } michael@0: #endif michael@0: if (!greFound) { michael@0: Output(false, "Could not find the Mozilla runtime.\n"); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: rv = XPCOMGlueStartup(greDir); michael@0: if (NS_FAILED(rv)) { michael@0: if (rv == NS_ERROR_OUT_OF_MEMORY) { michael@0: char applicationName[2000] = "this application"; michael@0: parser.GetString("App", "Name", applicationName, sizeof(applicationName)); michael@0: Output(true, "Not enough memory available to start %s.\n", michael@0: applicationName); michael@0: } else { michael@0: Output(true, "Couldn't load XPCOM.\n"); michael@0: } michael@0: return 1; michael@0: } michael@0: michael@0: static const nsDynamicFunctionLoad kXULFuncs[] = { michael@0: { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData }, michael@0: { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData }, michael@0: { "XRE_main", (NSFuncPtr*) &XRE_main }, michael@0: { nullptr, nullptr } michael@0: }; michael@0: michael@0: rv = XPCOMGlueLoadXULFunctions(kXULFuncs); michael@0: if (NS_FAILED(rv)) { michael@0: Output(true, "Couldn't load XRE functions.\n"); michael@0: return 1; michael@0: } michael@0: michael@0: NS_LogInit(); michael@0: michael@0: int retval; michael@0: michael@0: { // Scope COMPtr and AutoAppData michael@0: nsCOMPtr iniFile; michael@0: #ifdef XP_WIN michael@0: // On Windows iniPath is UTF-8 encoded so we need to convert it. michael@0: rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(iniPath), false, michael@0: getter_AddRefs(iniFile)); michael@0: #else michael@0: rv = NS_NewNativeLocalFile(nsDependentCString(iniPath), false, michael@0: getter_AddRefs(iniFile)); michael@0: #endif michael@0: if (NS_FAILED(rv)) { michael@0: Output(true, "Couldn't find application.ini file.\n"); michael@0: return 1; michael@0: } michael@0: michael@0: AutoAppData appData(iniFile); michael@0: if (!appData) { michael@0: Output(true, "Error: couldn't parse application.ini.\n"); michael@0: return 1; michael@0: } michael@0: michael@0: NS_ASSERTION(appData->directory, "Failed to get app directory."); michael@0: michael@0: if (!appData->xreDirectory) { michael@0: // chop "libxul.so" off the GRE path michael@0: lastSlash = strrchr(greDir, PATH_SEPARATOR_CHAR); michael@0: if (lastSlash) { michael@0: *lastSlash = '\0'; michael@0: } michael@0: #ifdef XP_WIN michael@0: // same as iniPath. michael@0: NS_NewLocalFile(NS_ConvertUTF8toUTF16(greDir), false, michael@0: &appData->xreDirectory); michael@0: #else michael@0: NS_NewNativeLocalFile(nsDependentCString(greDir), false, michael@0: &appData->xreDirectory); michael@0: #endif michael@0: } michael@0: michael@0: retval = XRE_main(argc, argv, appData, 0); michael@0: } michael@0: michael@0: NS_LogTerm(); michael@0: michael@0: return retval; michael@0: }