toolkit/components/commandlines/nsCommandLine.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     5 #include "nsICommandLineRunner.h"
     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"
    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"
    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
    39 #ifdef DEBUG_bsmedberg
    40 #define DEBUG_COMMANDLINE
    41 #endif
    43 #define NS_COMMANDLINE_CID \
    44   { 0x23bcc750, 0xdc20, 0x460b, { 0xb2, 0xd4, 0x74, 0xd8, 0xf5, 0x8d, 0x36, 0x15 } }
    46 class nsCommandLine MOZ_FINAL : public nsICommandLineRunner
    47 {
    48 public:
    49   NS_DECL_ISUPPORTS
    50   NS_DECL_NSICOMMANDLINE
    51   NS_DECL_NSICOMMANDLINERUNNER
    53   nsCommandLine();
    55 protected:
    56   ~nsCommandLine() { }
    58   typedef nsresult (*EnumerateHandlersCallback)(nsICommandLineHandler* aHandler,
    59 					nsICommandLine* aThis,
    60 					void *aClosure);
    61   typedef nsresult (*EnumerateValidatorsCallback)(nsICommandLineValidator* aValidator,
    62 					nsICommandLine* aThis,
    63 					void *aClosure);
    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);
    70   nsTArray<nsString>      mArgs;
    71   uint32_t                mState;
    72   nsCOMPtr<nsIFile>       mWorkingDir;
    73   nsCOMPtr<nsIDOMWindow>  mWindowContext;
    74   bool                    mPreventDefault;
    75 };
    77 nsCommandLine::nsCommandLine() :
    78   mState(STATE_INITIAL_LAUNCH),
    79   mPreventDefault(false)
    80 {
    82 }
    85 NS_IMPL_CLASSINFO(nsCommandLine, nullptr, 0, NS_COMMANDLINE_CID)
    86 NS_IMPL_ISUPPORTS_CI(nsCommandLine,
    87                      nsICommandLine,
    88                      nsICommandLineRunner)
    90 NS_IMETHODIMP
    91 nsCommandLine::GetLength(int32_t *aResult)
    92 {
    93   *aResult = int32_t(mArgs.Length());
    94   return NS_OK;
    95 }
    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));
   103   aResult = mArgs[aIndex];
   104   return NS_OK;
   105 }
   107 NS_IMETHODIMP
   108 nsCommandLine::FindFlag(const nsAString& aFlag, bool aCaseSensitive, int32_t *aResult)
   109 {
   110   NS_ENSURE_ARG(!aFlag.IsEmpty());
   112   nsDefaultStringComparator caseCmp;
   113   nsCaseInsensitiveStringComparator caseICmp;
   114   nsStringComparator& c = aCaseSensitive ?
   115     static_cast<nsStringComparator&>(caseCmp) :
   116     static_cast<nsStringComparator&>(caseICmp);
   118   for (uint32_t f = 0; f < mArgs.Length(); f++) {
   119     const nsString &arg = mArgs[f];
   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   }
   129   *aResult = -1;
   130   return NS_OK;
   131 }
   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());
   139   for (int32_t i = aEnd; i >= aStart; --i) {
   140     mArgs.RemoveElementAt(i);
   141   }
   143   return NS_OK;
   144 }
   146 NS_IMETHODIMP
   147 nsCommandLine::HandleFlag(const nsAString& aFlag, bool aCaseSensitive,
   148                           bool *aResult)
   149 {
   150   nsresult rv;
   152   int32_t found;
   153   rv = FindFlag(aFlag, aCaseSensitive, &found);
   154   NS_ENSURE_SUCCESS(rv, rv);
   156   if (found == -1) {
   157     *aResult = false;
   158     return NS_OK;
   159   }
   161   *aResult = true;
   162   RemoveArguments(found, found);
   164   return NS_OK;
   165 }
   167 NS_IMETHODIMP
   168 nsCommandLine::HandleFlagWithParam(const nsAString& aFlag, bool aCaseSensitive,
   169                                    nsAString& aResult)
   170 {
   171   nsresult rv;
   173   int32_t found;
   174   rv = FindFlag(aFlag, aCaseSensitive, &found);
   175   NS_ENSURE_SUCCESS(rv, rv);
   177   if (found == -1) {
   178     aResult.SetIsVoid(true);
   179     return NS_OK;
   180   }
   182   if (found == int32_t(mArgs.Length()) - 1) {
   183     return NS_ERROR_INVALID_ARG;
   184   }
   186   ++found;
   188   if (mArgs[found].First() == '-') {
   189     return NS_ERROR_INVALID_ARG;
   190   }
   192   aResult = mArgs[found];
   193   RemoveArguments(found - 1, found);
   195   return NS_OK;
   196 }
   198 NS_IMETHODIMP
   199 nsCommandLine::GetState(uint32_t *aResult)
   200 {
   201   *aResult = mState;
   202   return NS_OK;
   203 }
   205 NS_IMETHODIMP
   206 nsCommandLine::GetPreventDefault(bool *aResult)
   207 {
   208   *aResult = mPreventDefault;
   209   return NS_OK;
   210 }
   212 NS_IMETHODIMP
   213 nsCommandLine::SetPreventDefault(bool aValue)
   214 {
   215   mPreventDefault = aValue;
   216   return NS_OK;
   217 }
   219 NS_IMETHODIMP
   220 nsCommandLine::GetWorkingDirectory(nsIFile* *aResult)
   221 {
   222   NS_ENSURE_TRUE(mWorkingDir, NS_ERROR_NOT_INITIALIZED);
   224   NS_ADDREF(*aResult = mWorkingDir);
   225   return NS_OK;
   226 }
   228 NS_IMETHODIMP
   229 nsCommandLine::GetWindowContext(nsIDOMWindow* *aResult)
   230 {
   231   NS_IF_ADDREF(*aResult = mWindowContext);
   232   return NS_OK;
   233 }
   235 NS_IMETHODIMP
   236 nsCommandLine::SetWindowContext(nsIDOMWindow* aValue)
   237 {
   238   mWindowContext = aValue;
   239   return NS_OK;
   240 }
   242 NS_IMETHODIMP
   243 nsCommandLine::ResolveFile(const nsAString& aArgument, nsIFile* *aResult)
   244 {
   245   NS_ENSURE_TRUE(mWorkingDir, NS_ERROR_NOT_INITIALIZED);
   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.
   251   nsresult rv;
   253 #if defined(MOZ_WIDGET_COCOA)
   254   nsCOMPtr<nsILocalFileMac> lfm (do_QueryInterface(mWorkingDir));
   255   NS_ENSURE_TRUE(lfm, NS_ERROR_NO_INTERFACE);
   257   nsCOMPtr<nsILocalFileMac> newfile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
   258   NS_ENSURE_TRUE(newfile, NS_ERROR_OUT_OF_MEMORY);
   260   CFURLRef baseurl;
   261   rv = lfm->GetCFURL(&baseurl);
   262   NS_ENSURE_SUCCESS(rv, rv);
   264   nsAutoCString path;
   265   NS_CopyUnicodeToNative(aArgument, path);
   267   CFURLRef newurl =
   268     CFURLCreateFromFileSystemRepresentationRelativeToBase(nullptr, (const UInt8*) path.get(),
   269                                                           path.Length(),
   270                                                           true, baseurl);
   272   CFRelease(baseurl);
   274   rv = newfile->InitWithCFURL(newurl);
   275   CFRelease(newurl);
   276   if (NS_FAILED(rv)) return rv;
   278   NS_ADDREF(*aResult = newfile);
   279   return NS_OK;
   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);
   285   if (aArgument.First() == '/') {
   286     // absolute path
   287     rv = lf->InitWithPath(aArgument);
   288     if (NS_FAILED(rv)) return rv;
   290     NS_ADDREF(*aResult = lf);
   291     return NS_OK;
   292   }
   294   nsAutoCString nativeArg;
   295   NS_CopyUnicodeToNative(aArgument, nativeArg);
   297   nsAutoCString newpath;
   298   mWorkingDir->GetNativePath(newpath);
   300   newpath.Append('/');
   301   newpath.Append(nativeArg);
   303   rv = lf->InitWithNativePath(newpath);
   304   if (NS_FAILED(rv)) return rv;
   306   rv = lf->Normalize();
   307   if (NS_FAILED(rv)) return rv;
   309   NS_ADDREF(*aResult = lf);
   310   return NS_OK;
   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);
   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
   323     nsAutoString fullPath;
   324     mWorkingDir->GetPath(fullPath);
   326     fullPath.Append('\\');
   327     fullPath.Append(aArgument);
   329     WCHAR pathBuf[MAX_PATH];
   330     if (!_wfullpath(pathBuf, fullPath.get(), MAX_PATH))
   331       return NS_ERROR_FAILURE;
   333     rv = lf->InitWithPath(nsDependentString(pathBuf));
   334     if (NS_FAILED(rv)) return rv;
   335   }
   336   NS_ADDREF(*aResult = lf);
   337   return NS_OK;
   339 #else
   340 #error Need platform-specific logic here.
   341 #endif
   342 }
   344 NS_IMETHODIMP
   345 nsCommandLine::ResolveURI(const nsAString& aArgument, nsIURI* *aResult)
   346 {
   347   nsresult rv;
   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.
   352   nsCOMPtr<nsIIOService> io = do_GetIOService();
   353   NS_ENSURE_TRUE(io, NS_ERROR_OUT_OF_MEMORY);
   355   nsCOMPtr<nsIURI> workingDirURI;
   356   if (mWorkingDir) {
   357     io->NewFileURI(mWorkingDir, getter_AddRefs(workingDirURI));
   358   }
   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     }
   374     return io->NewFileURI(lf, aResult);
   375   }
   377   return io->NewURI(NS_ConvertUTF16toUTF8(aArgument),
   378                     nullptr,
   379                     workingDirURI,
   380                     aResult);
   381 }
   383 void
   384 nsCommandLine::appendArg(const char* arg)
   385 {
   386 #ifdef DEBUG_COMMANDLINE
   387   printf("Adding XP arg: %s\n", arg);
   388 #endif
   390   nsAutoString warg;
   391 #ifdef XP_WIN
   392   CopyUTF8toUTF16(nsDependentCString(arg), warg);
   393 #else
   394   NS_CopyNativeToUnicode(nsDependentCString(arg), warg);
   395 #endif
   397   mArgs.AppendElement(warg);
   398 }
   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;
   408   nsCOMPtr<nsIURI> uri;
   409   rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
   410   if (NS_FAILED(rv))
   411     return;
   413   uri->GetSpec(outURL);
   414 }
   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);
   422   int32_t i;
   424   mWorkingDir = aWorkingDir;
   426   // skip argv[0], we don't want it
   427   for (i = 1; i < argc; ++i) {
   428     const char* curarg = argv[i];
   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;
   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;
   456       char* dup = PL_strdup(curarg);
   457       if (!dup) return NS_ERROR_OUT_OF_MEMORY;
   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
   472     appendArg(curarg);
   473   }
   475   mState = aState;
   477   return NS_OK;
   478 }
   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);
   488   nsCOMPtr<nsIConsoleService> cs = do_GetService("@mozilla.org/consoleservice;1");
   489   if (cs)
   490     cs->LogStringMessage(msg);
   492   NS_Free(msg);
   493 }
   495 nsresult
   496 nsCommandLine::EnumerateHandlers(EnumerateHandlersCallback aCallback, void *aClosure)
   497 {
   498   nsresult rv;
   500   nsCOMPtr<nsICategoryManager> catman
   501     (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
   502   NS_ENSURE_TRUE(catman, NS_ERROR_UNEXPECTED);
   504   nsCOMPtr<nsISimpleEnumerator> entenum;
   505   rv = catman->EnumerateCategory("command-line-handler",
   506                                  getter_AddRefs(entenum));
   507   NS_ENSURE_SUCCESS(rv, rv);
   509   nsCOMPtr<nsIUTF8StringEnumerator> strenum (do_QueryInterface(entenum));
   510   NS_ENSURE_TRUE(strenum, NS_ERROR_UNEXPECTED);
   512   nsAutoCString entry;
   513   bool hasMore;
   514   while (NS_SUCCEEDED(strenum->HasMore(&hasMore)) && hasMore) {
   515     strenum->GetNext(entry);
   517     nsCString contractID;
   518     rv = catman->GetCategoryEntry("command-line-handler",
   519 				  entry.get(),
   520 				  getter_Copies(contractID));
   521     if (NS_FAILED(rv))
   522       continue;
   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     }
   531     rv = (aCallback)(clh, this, aClosure);
   532     if (rv == NS_ERROR_ABORT)
   533       break;
   535     rv = NS_OK;
   536   }
   538   return rv;
   539 }
   541 nsresult
   542 nsCommandLine::EnumerateValidators(EnumerateValidatorsCallback aCallback, void *aClosure)
   543 {
   544   nsresult rv;
   546   nsCOMPtr<nsICategoryManager> catman
   547     (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
   548   NS_ENSURE_TRUE(catman, NS_ERROR_UNEXPECTED);
   550   nsCOMPtr<nsISimpleEnumerator> entenum;
   551   rv = catman->EnumerateCategory("command-line-validator",
   552                                  getter_AddRefs(entenum));
   553   NS_ENSURE_SUCCESS(rv, rv);
   555   nsCOMPtr<nsIUTF8StringEnumerator> strenum (do_QueryInterface(entenum));
   556   NS_ENSURE_TRUE(strenum, NS_ERROR_UNEXPECTED);
   558   nsAutoCString entry;
   559   bool hasMore;
   560   while (NS_SUCCEEDED(strenum->HasMore(&hasMore)) && hasMore) {
   561     strenum->GetNext(entry);
   563     nsXPIDLCString contractID;
   564     rv = catman->GetCategoryEntry("command-line-validator",
   565 				  entry.get(),
   566 				  getter_Copies(contractID));
   567     if (!contractID)
   568       continue;
   570     nsCOMPtr<nsICommandLineValidator> clv(do_GetService(contractID.get()));
   571     if (!clv)
   572       continue;
   574     rv = (aCallback)(clv, this, aClosure);
   575     if (rv == NS_ERROR_ABORT)
   576       break;
   578     rv = NS_OK;
   579   }
   581   return rv;
   582 }
   584 static nsresult
   585 EnumValidate(nsICommandLineValidator* aValidator, nsICommandLine* aThis, void*)
   586 {
   587   return aValidator->Validate(aThis);
   588 }  
   590 static nsresult
   591 EnumRun(nsICommandLineHandler* aHandler, nsICommandLine* aThis, void*)
   592 {
   593   return aHandler->Handle(aThis);
   594 }  
   596 NS_IMETHODIMP
   597 nsCommandLine::Run()
   598 {
   599   nsresult rv;
   601   rv = EnumerateValidators(EnumValidate, nullptr);
   602   if (rv == NS_ERROR_ABORT)
   603     return rv;
   605   rv = EnumerateHandlers(EnumRun, nullptr);
   606   if (rv == NS_ERROR_ABORT)
   607     return rv;
   609   return NS_OK;
   610 }
   612 static nsresult
   613 EnumHelp(nsICommandLineHandler* aHandler, nsICommandLine* aThis, void* aClosure)
   614 {
   615   nsresult rv;
   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.");
   623     nsACString* totalText = reinterpret_cast<nsACString*>(aClosure);
   624     totalText->Append(text);
   625   }
   627   return NS_OK;
   628 }  
   630 NS_IMETHODIMP
   631 nsCommandLine::GetHelpText(nsACString& aResult)
   632 {
   633   EnumerateHandlers(EnumHelp, &aResult);
   635   return NS_OK;
   636 }
   638 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommandLine)
   640 NS_DEFINE_NAMED_CID(NS_COMMANDLINE_CID);
   642 static const mozilla::Module::CIDEntry kCommandLineCIDs[] = {
   643   { &kNS_COMMANDLINE_CID, false, nullptr, nsCommandLineConstructor },
   644   { nullptr }
   645 };
   647 static const mozilla::Module::ContractIDEntry kCommandLineContracts[] = {
   648   { "@mozilla.org/toolkit/command-line;1", &kNS_COMMANDLINE_CID },
   649   { nullptr }
   650 };
   652 static const mozilla::Module kCommandLineModule = {
   653   mozilla::Module::kVersion,
   654   kCommandLineCIDs,
   655   kCommandLineContracts
   656 };
   658 NSMODULE_DEFN(CommandLineModule) = &kCommandLineModule;

mercurial