|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "nsXPCOMGlue.h" |
|
6 #include "nsINIParser.h" |
|
7 #include "nsXPCOMPrivate.h" // for XP MAXPATHLEN |
|
8 #include "nsXULAppAPI.h" |
|
9 #include "nsIFile.h" |
|
10 |
|
11 #include <stdarg.h> |
|
12 |
|
13 #ifdef XP_WIN |
|
14 #include <windows.h> |
|
15 #include <io.h> |
|
16 #define snprintf _snprintf |
|
17 #define vsnprintf _vsnprintf |
|
18 #define strcasecmp _stricmp |
|
19 #define PATH_SEPARATOR_CHAR '\\' |
|
20 #define R_OK 04 |
|
21 #elif defined(XP_MACOSX) |
|
22 #include <unistd.h> |
|
23 #include <sys/stat.h> |
|
24 #include <CoreFoundation/CoreFoundation.h> |
|
25 #define PATH_SEPARATOR_CHAR '/' |
|
26 #else |
|
27 #include <unistd.h> |
|
28 #include <sys/types.h> |
|
29 #include <sys/stat.h> |
|
30 #define PATH_SEPARATOR_CHAR '/' |
|
31 #endif |
|
32 |
|
33 #ifdef XP_WIN |
|
34 #define XRE_DONT_PROTECT_DLL_LOAD |
|
35 #include "nsWindowsWMain.cpp" |
|
36 #endif |
|
37 |
|
38 #define VERSION_MAXLEN 128 |
|
39 |
|
40 static void Output(bool isError, const char *fmt, ... ) |
|
41 { |
|
42 va_list ap; |
|
43 va_start(ap, fmt); |
|
44 |
|
45 #if (defined(XP_WIN) && !MOZ_WINCONSOLE) |
|
46 char msg[2048]; |
|
47 |
|
48 vsnprintf(msg, sizeof(msg), fmt, ap); |
|
49 |
|
50 UINT flags = MB_OK; |
|
51 if (isError) |
|
52 flags |= MB_ICONERROR; |
|
53 else |
|
54 flags |= MB_ICONINFORMATION; |
|
55 |
|
56 wchar_t wide_msg[1024]; |
|
57 MultiByteToWideChar(CP_ACP, |
|
58 0, |
|
59 msg, |
|
60 -1, |
|
61 wide_msg, |
|
62 sizeof(wide_msg) / sizeof(wchar_t)); |
|
63 |
|
64 MessageBoxW(nullptr, wide_msg, L"XULRunner", flags); |
|
65 #else |
|
66 vfprintf(stderr, fmt, ap); |
|
67 #endif |
|
68 |
|
69 va_end(ap); |
|
70 } |
|
71 |
|
72 /** |
|
73 * Return true if |arg| matches the given argument name. |
|
74 */ |
|
75 static bool IsArg(const char* arg, const char* s) |
|
76 { |
|
77 if (*arg == '-') |
|
78 { |
|
79 if (*++arg == '-') |
|
80 ++arg; |
|
81 return !strcasecmp(arg, s); |
|
82 } |
|
83 |
|
84 #if defined(XP_WIN) |
|
85 if (*arg == '/') |
|
86 return !strcasecmp(++arg, s); |
|
87 #endif |
|
88 |
|
89 return false; |
|
90 } |
|
91 |
|
92 /** |
|
93 * Return true if |aDir| is a valid file/directory. |
|
94 */ |
|
95 static bool FolderExists(const char* aDir) |
|
96 { |
|
97 #ifdef XP_WIN |
|
98 wchar_t wideDir[MAX_PATH]; |
|
99 MultiByteToWideChar(CP_UTF8, 0, aDir, -1, wideDir, MAX_PATH); |
|
100 DWORD fileAttrs = GetFileAttributesW(wideDir); |
|
101 return fileAttrs != INVALID_FILE_ATTRIBUTES; |
|
102 #else |
|
103 return access(aDir, R_OK) == 0; |
|
104 #endif |
|
105 } |
|
106 |
|
107 static nsresult GetRealPath(const char* appDataFile, char* *aResult) |
|
108 { |
|
109 #ifdef XP_WIN |
|
110 wchar_t wAppDataFile[MAX_PATH]; |
|
111 wchar_t wIniPath[MAX_PATH]; |
|
112 MultiByteToWideChar(CP_UTF8, 0, appDataFile, -1, wAppDataFile, MAX_PATH); |
|
113 _wfullpath(wIniPath, wAppDataFile, MAX_PATH); |
|
114 WideCharToMultiByte(CP_UTF8, 0, wIniPath, -1, *aResult, MAX_PATH, 0, 0); |
|
115 #else |
|
116 struct stat fileStat; |
|
117 if (!realpath(appDataFile, *aResult) || stat(*aResult, &fileStat)) |
|
118 return NS_ERROR_FAILURE; |
|
119 #endif |
|
120 if (!*aResult || !**aResult) |
|
121 return NS_ERROR_FAILURE; |
|
122 |
|
123 return NS_OK; |
|
124 } |
|
125 |
|
126 class AutoAppData |
|
127 { |
|
128 public: |
|
129 AutoAppData(nsIFile* aINIFile) : mAppData(nullptr) { |
|
130 nsresult rv = XRE_CreateAppData(aINIFile, &mAppData); |
|
131 if (NS_FAILED(rv)) |
|
132 mAppData = nullptr; |
|
133 } |
|
134 ~AutoAppData() { |
|
135 if (mAppData) |
|
136 XRE_FreeAppData(mAppData); |
|
137 } |
|
138 |
|
139 operator nsXREAppData*() const { return mAppData; } |
|
140 nsXREAppData* operator -> () const { return mAppData; } |
|
141 |
|
142 private: |
|
143 nsXREAppData* mAppData; |
|
144 }; |
|
145 |
|
146 XRE_CreateAppDataType XRE_CreateAppData; |
|
147 XRE_FreeAppDataType XRE_FreeAppData; |
|
148 XRE_mainType XRE_main; |
|
149 |
|
150 int |
|
151 main(int argc, char **argv) |
|
152 { |
|
153 nsresult rv; |
|
154 char *lastSlash; |
|
155 |
|
156 char iniPath[MAXPATHLEN]; |
|
157 char tmpPath[MAXPATHLEN]; |
|
158 char greDir[MAXPATHLEN]; |
|
159 bool greFound = false; |
|
160 |
|
161 #if defined(XP_MACOSX) |
|
162 CFBundleRef appBundle = CFBundleGetMainBundle(); |
|
163 if (!appBundle) |
|
164 return 1; |
|
165 |
|
166 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(appBundle); |
|
167 if (!resourcesURL) |
|
168 return 1; |
|
169 |
|
170 CFURLRef absResourcesURL = CFURLCopyAbsoluteURL(resourcesURL); |
|
171 CFRelease(resourcesURL); |
|
172 if (!absResourcesURL) |
|
173 return 1; |
|
174 |
|
175 CFURLRef iniFileURL = |
|
176 CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, |
|
177 absResourcesURL, |
|
178 CFSTR("application.ini"), |
|
179 false); |
|
180 CFRelease(absResourcesURL); |
|
181 if (!iniFileURL) |
|
182 return 1; |
|
183 |
|
184 CFStringRef iniPathStr = |
|
185 CFURLCopyFileSystemPath(iniFileURL, kCFURLPOSIXPathStyle); |
|
186 CFRelease(iniFileURL); |
|
187 if (!iniPathStr) |
|
188 return 1; |
|
189 |
|
190 CFStringGetCString(iniPathStr, iniPath, sizeof(iniPath), |
|
191 kCFStringEncodingUTF8); |
|
192 CFRelease(iniPathStr); |
|
193 |
|
194 #else |
|
195 |
|
196 #ifdef XP_WIN |
|
197 wchar_t wide_path[MAX_PATH]; |
|
198 if (!::GetModuleFileNameW(nullptr, wide_path, MAX_PATH)) |
|
199 return 1; |
|
200 |
|
201 WideCharToMultiByte(CP_UTF8, 0, wide_path,-1, |
|
202 iniPath, MAX_PATH, nullptr, nullptr); |
|
203 |
|
204 #else |
|
205 // on unix, there is no official way to get the path of the current binary. |
|
206 // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to |
|
207 // multiple applications, we will try a series of techniques: |
|
208 // |
|
209 // 1) use realpath() on argv[0], which works unless we're loaded from the |
|
210 // PATH |
|
211 // 2) manually walk through the PATH and look for ourself |
|
212 // 3) give up |
|
213 |
|
214 struct stat fileStat; |
|
215 strncpy(tmpPath, argv[0], sizeof(tmpPath)); |
|
216 lastSlash = strrchr(tmpPath, '/'); |
|
217 if (lastSlash) { |
|
218 *lastSlash = 0; |
|
219 realpath(tmpPath, iniPath); |
|
220 } else { |
|
221 const char *path = getenv("PATH"); |
|
222 if (!path) |
|
223 return 1; |
|
224 |
|
225 char *pathdup = strdup(path); |
|
226 if (!pathdup) |
|
227 return 1; |
|
228 |
|
229 bool found = false; |
|
230 char *token = strtok(pathdup, ":"); |
|
231 while (token) { |
|
232 sprintf(tmpPath, "%s/%s", token, argv[0]); |
|
233 if (stat(tmpPath, &fileStat) == 0) { |
|
234 found = true; |
|
235 lastSlash = strrchr(tmpPath, '/'); |
|
236 *lastSlash = 0; |
|
237 realpath(tmpPath, iniPath); |
|
238 break; |
|
239 } |
|
240 token = strtok(nullptr, ":"); |
|
241 } |
|
242 free (pathdup); |
|
243 if (!found) |
|
244 return 1; |
|
245 } |
|
246 lastSlash = iniPath + strlen(iniPath); |
|
247 *lastSlash = '/'; |
|
248 #endif |
|
249 |
|
250 #ifndef XP_UNIX |
|
251 lastSlash = strrchr(iniPath, PATH_SEPARATOR_CHAR); |
|
252 if (!lastSlash) |
|
253 return 1; |
|
254 #endif |
|
255 |
|
256 *(++lastSlash) = '\0'; |
|
257 |
|
258 // On Linux/Win, look for XULRunner in appdir/xulrunner |
|
259 |
|
260 snprintf(greDir, sizeof(greDir), |
|
261 "%sxulrunner" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL, |
|
262 iniPath); |
|
263 |
|
264 greFound = FolderExists(greDir); |
|
265 |
|
266 #ifdef XP_UNIX |
|
267 if (greFound) { |
|
268 char resolved_greDir[MAXPATHLEN] = ""; |
|
269 if (realpath(greDir, resolved_greDir) && *resolved_greDir) { |
|
270 strncpy(greDir, resolved_greDir, MAXPATHLEN); |
|
271 } |
|
272 } |
|
273 #endif |
|
274 |
|
275 strncpy(lastSlash, "application.ini", sizeof(iniPath) - (lastSlash - iniPath)); |
|
276 |
|
277 #endif |
|
278 |
|
279 // If -app parameter was passed in, it is now time to take it under |
|
280 // consideration. |
|
281 const char *appDataFile; |
|
282 appDataFile = getenv("XUL_APP_FILE"); |
|
283 if (!appDataFile || !*appDataFile) |
|
284 if (argc > 1 && IsArg(argv[1], "app")) { |
|
285 if (argc == 2) { |
|
286 Output(false, "specify APP-FILE (optional)\n"); |
|
287 return 1; |
|
288 } |
|
289 argv[1] = argv[0]; |
|
290 ++argv; |
|
291 --argc; |
|
292 |
|
293 appDataFile = argv[1]; |
|
294 argv[1] = argv[0]; |
|
295 ++argv; |
|
296 --argc; |
|
297 |
|
298 char kAppEnv[MAXPATHLEN]; |
|
299 snprintf(kAppEnv, MAXPATHLEN, "XUL_APP_FILE=%s", appDataFile); |
|
300 if (putenv(kAppEnv)) |
|
301 Output(false, "Couldn't set %s.\n", kAppEnv); |
|
302 |
|
303 char *result = (char*) calloc(sizeof(char), MAXPATHLEN); |
|
304 if (NS_FAILED(GetRealPath(appDataFile, &result))) { |
|
305 Output(true, "Invalid application.ini path.\n"); |
|
306 return 1; |
|
307 } |
|
308 |
|
309 // We have a valid application.ini path passed in to the -app parameter |
|
310 // but not yet a valid greDir, so lets look for it also on the same folder |
|
311 // as the stub. |
|
312 if (!greFound) { |
|
313 lastSlash = strrchr(iniPath, PATH_SEPARATOR_CHAR); |
|
314 if (!lastSlash) |
|
315 return 1; |
|
316 |
|
317 *(++lastSlash) = '\0'; |
|
318 |
|
319 snprintf(greDir, sizeof(greDir), "%s" XPCOM_DLL, iniPath); |
|
320 greFound = FolderExists(greDir); |
|
321 } |
|
322 |
|
323 // copy it back. |
|
324 strcpy(iniPath, result); |
|
325 } |
|
326 |
|
327 nsINIParser parser; |
|
328 rv = parser.Init(iniPath); |
|
329 if (NS_FAILED(rv)) { |
|
330 fprintf(stderr, "Could not read application.ini\n"); |
|
331 return 1; |
|
332 } |
|
333 |
|
334 if (!greFound) { |
|
335 #ifdef XP_MACOSX |
|
336 // Check for <bundle>/Contents/Frameworks/XUL.framework/Versions/Current/libmozglue.dylib |
|
337 CFURLRef fwurl = CFBundleCopyPrivateFrameworksURL(appBundle); |
|
338 CFURLRef absfwurl = nullptr; |
|
339 if (fwurl) { |
|
340 absfwurl = CFURLCopyAbsoluteURL(fwurl); |
|
341 CFRelease(fwurl); |
|
342 } |
|
343 |
|
344 if (absfwurl) { |
|
345 CFURLRef xulurl = |
|
346 CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl, |
|
347 CFSTR("XUL.framework/Versions/Current"), |
|
348 true); |
|
349 |
|
350 if (xulurl) { |
|
351 CFURLRef xpcomurl = |
|
352 CFURLCreateCopyAppendingPathComponent(nullptr, xulurl, |
|
353 CFSTR("libmozglue.dylib"), |
|
354 false); |
|
355 |
|
356 if (xpcomurl) { |
|
357 char tbuffer[MAXPATHLEN]; |
|
358 |
|
359 if (CFURLGetFileSystemRepresentation(xpcomurl, true, |
|
360 (UInt8*) tbuffer, |
|
361 sizeof(tbuffer)) && |
|
362 access(tbuffer, R_OK | X_OK) == 0) { |
|
363 if (realpath(tbuffer, greDir)) { |
|
364 greFound = true; |
|
365 } |
|
366 else { |
|
367 greDir[0] = '\0'; |
|
368 } |
|
369 } |
|
370 |
|
371 CFRelease(xpcomurl); |
|
372 } |
|
373 |
|
374 CFRelease(xulurl); |
|
375 } |
|
376 |
|
377 CFRelease(absfwurl); |
|
378 } |
|
379 #endif |
|
380 if (!greFound) { |
|
381 Output(false, "Could not find the Mozilla runtime.\n"); |
|
382 return 1; |
|
383 } |
|
384 } |
|
385 |
|
386 rv = XPCOMGlueStartup(greDir); |
|
387 if (NS_FAILED(rv)) { |
|
388 if (rv == NS_ERROR_OUT_OF_MEMORY) { |
|
389 char applicationName[2000] = "this application"; |
|
390 parser.GetString("App", "Name", applicationName, sizeof(applicationName)); |
|
391 Output(true, "Not enough memory available to start %s.\n", |
|
392 applicationName); |
|
393 } else { |
|
394 Output(true, "Couldn't load XPCOM.\n"); |
|
395 } |
|
396 return 1; |
|
397 } |
|
398 |
|
399 static const nsDynamicFunctionLoad kXULFuncs[] = { |
|
400 { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData }, |
|
401 { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData }, |
|
402 { "XRE_main", (NSFuncPtr*) &XRE_main }, |
|
403 { nullptr, nullptr } |
|
404 }; |
|
405 |
|
406 rv = XPCOMGlueLoadXULFunctions(kXULFuncs); |
|
407 if (NS_FAILED(rv)) { |
|
408 Output(true, "Couldn't load XRE functions.\n"); |
|
409 return 1; |
|
410 } |
|
411 |
|
412 NS_LogInit(); |
|
413 |
|
414 int retval; |
|
415 |
|
416 { // Scope COMPtr and AutoAppData |
|
417 nsCOMPtr<nsIFile> iniFile; |
|
418 #ifdef XP_WIN |
|
419 // On Windows iniPath is UTF-8 encoded so we need to convert it. |
|
420 rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(iniPath), false, |
|
421 getter_AddRefs(iniFile)); |
|
422 #else |
|
423 rv = NS_NewNativeLocalFile(nsDependentCString(iniPath), false, |
|
424 getter_AddRefs(iniFile)); |
|
425 #endif |
|
426 if (NS_FAILED(rv)) { |
|
427 Output(true, "Couldn't find application.ini file.\n"); |
|
428 return 1; |
|
429 } |
|
430 |
|
431 AutoAppData appData(iniFile); |
|
432 if (!appData) { |
|
433 Output(true, "Error: couldn't parse application.ini.\n"); |
|
434 return 1; |
|
435 } |
|
436 |
|
437 NS_ASSERTION(appData->directory, "Failed to get app directory."); |
|
438 |
|
439 if (!appData->xreDirectory) { |
|
440 // chop "libxul.so" off the GRE path |
|
441 lastSlash = strrchr(greDir, PATH_SEPARATOR_CHAR); |
|
442 if (lastSlash) { |
|
443 *lastSlash = '\0'; |
|
444 } |
|
445 #ifdef XP_WIN |
|
446 // same as iniPath. |
|
447 NS_NewLocalFile(NS_ConvertUTF8toUTF16(greDir), false, |
|
448 &appData->xreDirectory); |
|
449 #else |
|
450 NS_NewNativeLocalFile(nsDependentCString(greDir), false, |
|
451 &appData->xreDirectory); |
|
452 #endif |
|
453 } |
|
454 |
|
455 retval = XRE_main(argc, argv, appData, 0); |
|
456 } |
|
457 |
|
458 NS_LogTerm(); |
|
459 |
|
460 return retval; |
|
461 } |