toolkit/components/commandlines/nsCommandLine.cpp

branch
TOR_BUG_9701
changeset 14
925c144e1f1f
equal deleted inserted replaced
-1:000000000000 0:5554f99707ae
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 "nsICommandLineRunner.h"
6
7 #include "nsICategoryManager.h"
8 #include "nsICommandLineHandler.h"
9 #include "nsICommandLineValidator.h"
10 #include "nsIConsoleService.h"
11 #include "nsIClassInfoImpl.h"
12 #include "nsIDOMWindow.h"
13 #include "nsIFile.h"
14 #include "nsISimpleEnumerator.h"
15 #include "nsIStringEnumerator.h"
16
17 #include "nsCOMPtr.h"
18 #include "mozilla/ModuleUtils.h"
19 #include "nsISupportsImpl.h"
20 #include "nsNativeCharsetUtils.h"
21 #include "nsNetUtil.h"
22 #include "nsUnicharUtils.h"
23 #include "nsTArray.h"
24 #include "nsTextFormatter.h"
25 #include "nsXPCOMCID.h"
26 #include "plstr.h"
27 #include "mozilla/Attributes.h"
28
29 #ifdef MOZ_WIDGET_COCOA
30 #include <CoreFoundation/CoreFoundation.h>
31 #include "nsILocalFileMac.h"
32 #elif defined(XP_WIN)
33 #include <windows.h>
34 #include <shlobj.h>
35 #elif defined(XP_UNIX)
36 #include <unistd.h>
37 #endif
38
39 #ifdef DEBUG_bsmedberg
40 #define DEBUG_COMMANDLINE
41 #endif
42
43 #define NS_COMMANDLINE_CID \
44 { 0x23bcc750, 0xdc20, 0x460b, { 0xb2, 0xd4, 0x74, 0xd8, 0xf5, 0x8d, 0x36, 0x15 } }
45
46 class nsCommandLine MOZ_FINAL : public nsICommandLineRunner
47 {
48 public:
49 NS_DECL_ISUPPORTS
50 NS_DECL_NSICOMMANDLINE
51 NS_DECL_NSICOMMANDLINERUNNER
52
53 nsCommandLine();
54
55 protected:
56 ~nsCommandLine() { }
57
58 typedef nsresult (*EnumerateHandlersCallback)(nsICommandLineHandler* aHandler,
59 nsICommandLine* aThis,
60 void *aClosure);
61 typedef nsresult (*EnumerateValidatorsCallback)(nsICommandLineValidator* aValidator,
62 nsICommandLine* aThis,
63 void *aClosure);
64
65 void appendArg(const char* arg);
66 void resolveShortcutURL(nsIFile* aFile, nsACString& outURL);
67 nsresult EnumerateHandlers(EnumerateHandlersCallback aCallback, void *aClosure);
68 nsresult EnumerateValidators(EnumerateValidatorsCallback aCallback, void *aClosure);
69
70 nsTArray<nsString> mArgs;
71 uint32_t mState;
72 nsCOMPtr<nsIFile> mWorkingDir;
73 nsCOMPtr<nsIDOMWindow> mWindowContext;
74 bool mPreventDefault;
75 };
76
77 nsCommandLine::nsCommandLine() :
78 mState(STATE_INITIAL_LAUNCH),
79 mPreventDefault(false)
80 {
81
82 }
83
84
85 NS_IMPL_CLASSINFO(nsCommandLine, nullptr, 0, NS_COMMANDLINE_CID)
86 NS_IMPL_ISUPPORTS_CI(nsCommandLine,
87 nsICommandLine,
88 nsICommandLineRunner)
89
90 NS_IMETHODIMP
91 nsCommandLine::GetLength(int32_t *aResult)
92 {
93 *aResult = int32_t(mArgs.Length());
94 return NS_OK;
95 }
96
97 NS_IMETHODIMP
98 nsCommandLine::GetArgument(int32_t aIndex, nsAString& aResult)
99 {
100 NS_ENSURE_ARG_MIN(aIndex, 0);
101 NS_ENSURE_ARG_MAX(aIndex, int32_t(mArgs.Length() - 1));
102
103 aResult = mArgs[aIndex];
104 return NS_OK;
105 }
106
107 NS_IMETHODIMP
108 nsCommandLine::FindFlag(const nsAString& aFlag, bool aCaseSensitive, int32_t *aResult)
109 {
110 NS_ENSURE_ARG(!aFlag.IsEmpty());
111
112 nsDefaultStringComparator caseCmp;
113 nsCaseInsensitiveStringComparator caseICmp;
114 nsStringComparator& c = aCaseSensitive ?
115 static_cast<nsStringComparator&>(caseCmp) :
116 static_cast<nsStringComparator&>(caseICmp);
117
118 for (uint32_t f = 0; f < mArgs.Length(); f++) {
119 const nsString &arg = mArgs[f];
120
121 if (arg.Length() >= 2 && arg.First() == char16_t('-')) {
122 if (aFlag.Equals(Substring(arg, 1), c)) {
123 *aResult = f;
124 return NS_OK;
125 }
126 }
127 }
128
129 *aResult = -1;
130 return NS_OK;
131 }
132
133 NS_IMETHODIMP
134 nsCommandLine::RemoveArguments(int32_t aStart, int32_t aEnd)
135 {
136 NS_ENSURE_ARG_MIN(aStart, 0);
137 NS_ENSURE_ARG_MAX(uint32_t(aEnd) + 1, mArgs.Length());
138
139 for (int32_t i = aEnd; i >= aStart; --i) {
140 mArgs.RemoveElementAt(i);
141 }
142
143 return NS_OK;
144 }
145
146 NS_IMETHODIMP
147 nsCommandLine::HandleFlag(const nsAString& aFlag, bool aCaseSensitive,
148 bool *aResult)
149 {
150 nsresult rv;
151
152 int32_t found;
153 rv = FindFlag(aFlag, aCaseSensitive, &found);
154 NS_ENSURE_SUCCESS(rv, rv);
155
156 if (found == -1) {
157 *aResult = false;
158 return NS_OK;
159 }
160
161 *aResult = true;
162 RemoveArguments(found, found);
163
164 return NS_OK;
165 }
166
167 NS_IMETHODIMP
168 nsCommandLine::HandleFlagWithParam(const nsAString& aFlag, bool aCaseSensitive,
169 nsAString& aResult)
170 {
171 nsresult rv;
172
173 int32_t found;
174 rv = FindFlag(aFlag, aCaseSensitive, &found);
175 NS_ENSURE_SUCCESS(rv, rv);
176
177 if (found == -1) {
178 aResult.SetIsVoid(true);
179 return NS_OK;
180 }
181
182 if (found == int32_t(mArgs.Length()) - 1) {
183 return NS_ERROR_INVALID_ARG;
184 }
185
186 ++found;
187
188 if (mArgs[found].First() == '-') {
189 return NS_ERROR_INVALID_ARG;
190 }
191
192 aResult = mArgs[found];
193 RemoveArguments(found - 1, found);
194
195 return NS_OK;
196 }
197
198 NS_IMETHODIMP
199 nsCommandLine::GetState(uint32_t *aResult)
200 {
201 *aResult = mState;
202 return NS_OK;
203 }
204
205 NS_IMETHODIMP
206 nsCommandLine::GetPreventDefault(bool *aResult)
207 {
208 *aResult = mPreventDefault;
209 return NS_OK;
210 }
211
212 NS_IMETHODIMP
213 nsCommandLine::SetPreventDefault(bool aValue)
214 {
215 mPreventDefault = aValue;
216 return NS_OK;
217 }
218
219 NS_IMETHODIMP
220 nsCommandLine::GetWorkingDirectory(nsIFile* *aResult)
221 {
222 NS_ENSURE_TRUE(mWorkingDir, NS_ERROR_NOT_INITIALIZED);
223
224 NS_ADDREF(*aResult = mWorkingDir);
225 return NS_OK;
226 }
227
228 NS_IMETHODIMP
229 nsCommandLine::GetWindowContext(nsIDOMWindow* *aResult)
230 {
231 NS_IF_ADDREF(*aResult = mWindowContext);
232 return NS_OK;
233 }
234
235 NS_IMETHODIMP
236 nsCommandLine::SetWindowContext(nsIDOMWindow* aValue)
237 {
238 mWindowContext = aValue;
239 return NS_OK;
240 }
241
242 NS_IMETHODIMP
243 nsCommandLine::ResolveFile(const nsAString& aArgument, nsIFile* *aResult)
244 {
245 NS_ENSURE_TRUE(mWorkingDir, NS_ERROR_NOT_INITIALIZED);
246
247 // This is some seriously screwed-up code. nsIFile.appendRelativeNativePath
248 // explicitly does not accept .. or . path parts, but that is exactly what we
249 // need here. So we hack around it.
250
251 nsresult rv;
252
253 #if defined(MOZ_WIDGET_COCOA)
254 nsCOMPtr<nsILocalFileMac> lfm (do_QueryInterface(mWorkingDir));
255 NS_ENSURE_TRUE(lfm, NS_ERROR_NO_INTERFACE);
256
257 nsCOMPtr<nsILocalFileMac> newfile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
258 NS_ENSURE_TRUE(newfile, NS_ERROR_OUT_OF_MEMORY);
259
260 CFURLRef baseurl;
261 rv = lfm->GetCFURL(&baseurl);
262 NS_ENSURE_SUCCESS(rv, rv);
263
264 nsAutoCString path;
265 NS_CopyUnicodeToNative(aArgument, path);
266
267 CFURLRef newurl =
268 CFURLCreateFromFileSystemRepresentationRelativeToBase(nullptr, (const UInt8*) path.get(),
269 path.Length(),
270 true, baseurl);
271
272 CFRelease(baseurl);
273
274 rv = newfile->InitWithCFURL(newurl);
275 CFRelease(newurl);
276 if (NS_FAILED(rv)) return rv;
277
278 NS_ADDREF(*aResult = newfile);
279 return NS_OK;
280
281 #elif defined(XP_UNIX)
282 nsCOMPtr<nsIFile> lf (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
283 NS_ENSURE_TRUE(lf, NS_ERROR_OUT_OF_MEMORY);
284
285 if (aArgument.First() == '/') {
286 // absolute path
287 rv = lf->InitWithPath(aArgument);
288 if (NS_FAILED(rv)) return rv;
289
290 NS_ADDREF(*aResult = lf);
291 return NS_OK;
292 }
293
294 nsAutoCString nativeArg;
295 NS_CopyUnicodeToNative(aArgument, nativeArg);
296
297 nsAutoCString newpath;
298 mWorkingDir->GetNativePath(newpath);
299
300 newpath.Append('/');
301 newpath.Append(nativeArg);
302
303 rv = lf->InitWithNativePath(newpath);
304 if (NS_FAILED(rv)) return rv;
305
306 rv = lf->Normalize();
307 if (NS_FAILED(rv)) return rv;
308
309 NS_ADDREF(*aResult = lf);
310 return NS_OK;
311
312 #elif defined(XP_WIN32)
313 nsCOMPtr<nsIFile> lf (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
314 NS_ENSURE_TRUE(lf, NS_ERROR_OUT_OF_MEMORY);
315
316 rv = lf->InitWithPath(aArgument);
317 if (NS_FAILED(rv)) {
318 // If it's a relative path, the Init is *going* to fail. We use string magic and
319 // win32 _fullpath. Note that paths of the form "\Relative\To\CurDrive" are
320 // going to fail, and I haven't figured out a way to work around this without
321 // the PathCombine() function, which is not available in plain win95/nt4
322
323 nsAutoString fullPath;
324 mWorkingDir->GetPath(fullPath);
325
326 fullPath.Append('\\');
327 fullPath.Append(aArgument);
328
329 WCHAR pathBuf[MAX_PATH];
330 if (!_wfullpath(pathBuf, fullPath.get(), MAX_PATH))
331 return NS_ERROR_FAILURE;
332
333 rv = lf->InitWithPath(nsDependentString(pathBuf));
334 if (NS_FAILED(rv)) return rv;
335 }
336 NS_ADDREF(*aResult = lf);
337 return NS_OK;
338
339 #else
340 #error Need platform-specific logic here.
341 #endif
342 }
343
344 NS_IMETHODIMP
345 nsCommandLine::ResolveURI(const nsAString& aArgument, nsIURI* *aResult)
346 {
347 nsresult rv;
348
349 // First, we try to init the argument as an absolute file path. If this doesn't
350 // work, it is an absolute or relative URI.
351
352 nsCOMPtr<nsIIOService> io = do_GetIOService();
353 NS_ENSURE_TRUE(io, NS_ERROR_OUT_OF_MEMORY);
354
355 nsCOMPtr<nsIURI> workingDirURI;
356 if (mWorkingDir) {
357 io->NewFileURI(mWorkingDir, getter_AddRefs(workingDirURI));
358 }
359
360 nsCOMPtr<nsIFile> lf (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
361 rv = lf->InitWithPath(aArgument);
362 if (NS_SUCCEEDED(rv)) {
363 lf->Normalize();
364 nsAutoCString url;
365 // Try to resolve the url for .url files.
366 resolveShortcutURL(lf, url);
367 if (!url.IsEmpty()) {
368 return io->NewURI(url,
369 nullptr,
370 workingDirURI,
371 aResult);
372 }
373
374 return io->NewFileURI(lf, aResult);
375 }
376
377 return io->NewURI(NS_ConvertUTF16toUTF8(aArgument),
378 nullptr,
379 workingDirURI,
380 aResult);
381 }
382
383 void
384 nsCommandLine::appendArg(const char* arg)
385 {
386 #ifdef DEBUG_COMMANDLINE
387 printf("Adding XP arg: %s\n", arg);
388 #endif
389
390 nsAutoString warg;
391 #ifdef XP_WIN
392 CopyUTF8toUTF16(nsDependentCString(arg), warg);
393 #else
394 NS_CopyNativeToUnicode(nsDependentCString(arg), warg);
395 #endif
396
397 mArgs.AppendElement(warg);
398 }
399
400 void
401 nsCommandLine::resolveShortcutURL(nsIFile* aFile, nsACString& outURL)
402 {
403 nsCOMPtr<nsIFileProtocolHandler> fph;
404 nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
405 if (NS_FAILED(rv))
406 return;
407
408 nsCOMPtr<nsIURI> uri;
409 rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
410 if (NS_FAILED(rv))
411 return;
412
413 uri->GetSpec(outURL);
414 }
415
416 NS_IMETHODIMP
417 nsCommandLine::Init(int32_t argc, const char* const* argv, nsIFile* aWorkingDir,
418 uint32_t aState)
419 {
420 NS_ENSURE_ARG_MAX(aState, 2);
421
422 int32_t i;
423
424 mWorkingDir = aWorkingDir;
425
426 // skip argv[0], we don't want it
427 for (i = 1; i < argc; ++i) {
428 const char* curarg = argv[i];
429
430 #ifdef DEBUG_COMMANDLINE
431 printf("Testing native arg %i: '%s'\n", i, curarg);
432 #endif
433 #if defined(XP_WIN)
434 if (*curarg == '/') {
435 char* dup = PL_strdup(curarg);
436 if (!dup) return NS_ERROR_OUT_OF_MEMORY;
437
438 *dup = '-';
439 char* colon = PL_strchr(dup, ':');
440 if (colon) {
441 *colon = '\0';
442 appendArg(dup);
443 appendArg(colon+1);
444 } else {
445 appendArg(dup);
446 }
447 PL_strfree(dup);
448 continue;
449 }
450 #endif
451 #ifdef XP_UNIX
452 if (*curarg == '-' &&
453 *(curarg+1) == '-') {
454 ++curarg;
455
456 char* dup = PL_strdup(curarg);
457 if (!dup) return NS_ERROR_OUT_OF_MEMORY;
458
459 char* eq = PL_strchr(dup, '=');
460 if (eq) {
461 *eq = '\0';
462 appendArg(dup);
463 appendArg(eq + 1);
464 } else {
465 appendArg(dup);
466 }
467 PL_strfree(dup);
468 continue;
469 }
470 #endif
471
472 appendArg(curarg);
473 }
474
475 mState = aState;
476
477 return NS_OK;
478 }
479
480 static void
481 LogConsoleMessage(const char16_t* fmt, ...)
482 {
483 va_list args;
484 va_start(args, fmt);
485 char16_t* msg = nsTextFormatter::vsmprintf(fmt, args);
486 va_end(args);
487
488 nsCOMPtr<nsIConsoleService> cs = do_GetService("@mozilla.org/consoleservice;1");
489 if (cs)
490 cs->LogStringMessage(msg);
491
492 NS_Free(msg);
493 }
494
495 nsresult
496 nsCommandLine::EnumerateHandlers(EnumerateHandlersCallback aCallback, void *aClosure)
497 {
498 nsresult rv;
499
500 nsCOMPtr<nsICategoryManager> catman
501 (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
502 NS_ENSURE_TRUE(catman, NS_ERROR_UNEXPECTED);
503
504 nsCOMPtr<nsISimpleEnumerator> entenum;
505 rv = catman->EnumerateCategory("command-line-handler",
506 getter_AddRefs(entenum));
507 NS_ENSURE_SUCCESS(rv, rv);
508
509 nsCOMPtr<nsIUTF8StringEnumerator> strenum (do_QueryInterface(entenum));
510 NS_ENSURE_TRUE(strenum, NS_ERROR_UNEXPECTED);
511
512 nsAutoCString entry;
513 bool hasMore;
514 while (NS_SUCCEEDED(strenum->HasMore(&hasMore)) && hasMore) {
515 strenum->GetNext(entry);
516
517 nsCString contractID;
518 rv = catman->GetCategoryEntry("command-line-handler",
519 entry.get(),
520 getter_Copies(contractID));
521 if (NS_FAILED(rv))
522 continue;
523
524 nsCOMPtr<nsICommandLineHandler> clh(do_GetService(contractID.get()));
525 if (!clh) {
526 LogConsoleMessage(MOZ_UTF16("Contract ID '%s' was registered as a command line handler for entry '%s', but could not be created."),
527 contractID.get(), entry.get());
528 continue;
529 }
530
531 rv = (aCallback)(clh, this, aClosure);
532 if (rv == NS_ERROR_ABORT)
533 break;
534
535 rv = NS_OK;
536 }
537
538 return rv;
539 }
540
541 nsresult
542 nsCommandLine::EnumerateValidators(EnumerateValidatorsCallback aCallback, void *aClosure)
543 {
544 nsresult rv;
545
546 nsCOMPtr<nsICategoryManager> catman
547 (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
548 NS_ENSURE_TRUE(catman, NS_ERROR_UNEXPECTED);
549
550 nsCOMPtr<nsISimpleEnumerator> entenum;
551 rv = catman->EnumerateCategory("command-line-validator",
552 getter_AddRefs(entenum));
553 NS_ENSURE_SUCCESS(rv, rv);
554
555 nsCOMPtr<nsIUTF8StringEnumerator> strenum (do_QueryInterface(entenum));
556 NS_ENSURE_TRUE(strenum, NS_ERROR_UNEXPECTED);
557
558 nsAutoCString entry;
559 bool hasMore;
560 while (NS_SUCCEEDED(strenum->HasMore(&hasMore)) && hasMore) {
561 strenum->GetNext(entry);
562
563 nsXPIDLCString contractID;
564 rv = catman->GetCategoryEntry("command-line-validator",
565 entry.get(),
566 getter_Copies(contractID));
567 if (!contractID)
568 continue;
569
570 nsCOMPtr<nsICommandLineValidator> clv(do_GetService(contractID.get()));
571 if (!clv)
572 continue;
573
574 rv = (aCallback)(clv, this, aClosure);
575 if (rv == NS_ERROR_ABORT)
576 break;
577
578 rv = NS_OK;
579 }
580
581 return rv;
582 }
583
584 static nsresult
585 EnumValidate(nsICommandLineValidator* aValidator, nsICommandLine* aThis, void*)
586 {
587 return aValidator->Validate(aThis);
588 }
589
590 static nsresult
591 EnumRun(nsICommandLineHandler* aHandler, nsICommandLine* aThis, void*)
592 {
593 return aHandler->Handle(aThis);
594 }
595
596 NS_IMETHODIMP
597 nsCommandLine::Run()
598 {
599 nsresult rv;
600
601 rv = EnumerateValidators(EnumValidate, nullptr);
602 if (rv == NS_ERROR_ABORT)
603 return rv;
604
605 rv = EnumerateHandlers(EnumRun, nullptr);
606 if (rv == NS_ERROR_ABORT)
607 return rv;
608
609 return NS_OK;
610 }
611
612 static nsresult
613 EnumHelp(nsICommandLineHandler* aHandler, nsICommandLine* aThis, void* aClosure)
614 {
615 nsresult rv;
616
617 nsCString text;
618 rv = aHandler->GetHelpInfo(text);
619 if (NS_SUCCEEDED(rv)) {
620 NS_ASSERTION(text.Length() == 0 || text.Last() == '\n',
621 "Help text from command line handlers should end in a newline.");
622
623 nsACString* totalText = reinterpret_cast<nsACString*>(aClosure);
624 totalText->Append(text);
625 }
626
627 return NS_OK;
628 }
629
630 NS_IMETHODIMP
631 nsCommandLine::GetHelpText(nsACString& aResult)
632 {
633 EnumerateHandlers(EnumHelp, &aResult);
634
635 return NS_OK;
636 }
637
638 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommandLine)
639
640 NS_DEFINE_NAMED_CID(NS_COMMANDLINE_CID);
641
642 static const mozilla::Module::CIDEntry kCommandLineCIDs[] = {
643 { &kNS_COMMANDLINE_CID, false, nullptr, nsCommandLineConstructor },
644 { nullptr }
645 };
646
647 static const mozilla::Module::ContractIDEntry kCommandLineContracts[] = {
648 { "@mozilla.org/toolkit/command-line;1", &kNS_COMMANDLINE_CID },
649 { nullptr }
650 };
651
652 static const mozilla::Module kCommandLineModule = {
653 mozilla::Module::kVersion,
654 kCommandLineCIDs,
655 kCommandLineContracts
656 };
657
658 NSMODULE_DEFN(CommandLineModule) = &kCommandLineModule;

mercurial