Bug 1706501 - Make CommandLineParserWin::HandleCommandLine take nsTSubstring. r=mossop

Differential Revision: https://phabricator.services.mozilla.com/D113060
This commit is contained in:
Toshihito Kikuchi 2021-04-27 18:20:51 +00:00
parent a4c2f8da8d
commit 96461a9f97
4 changed files with 55 additions and 46 deletions

View File

@ -49,7 +49,7 @@ WinRemoteMessageSender::WinRemoteMessageSender(const wchar_t* aCommandLine,
COPYDATASTRUCT* WinRemoteMessageSender::CopyData() { return &mData; } COPYDATASTRUCT* WinRemoteMessageSender::CopyData() { return &mData; }
nsresult WinRemoteMessageReceiver::ParseV0(char* aBuffer) { nsresult WinRemoteMessageReceiver::ParseV0(const nsACString& aBuffer) {
CommandLineParserWin<char> parser; CommandLineParserWin<char> parser;
parser.HandleCommandLine(aBuffer); parser.HandleCommandLine(aBuffer);
@ -58,42 +58,33 @@ nsresult WinRemoteMessageReceiver::ParseV0(char* aBuffer) {
nsICommandLine::STATE_REMOTE_AUTO); nsICommandLine::STATE_REMOTE_AUTO);
} }
nsresult WinRemoteMessageReceiver::ParseV1(char* aBuffer) { nsresult WinRemoteMessageReceiver::ParseV1(const nsACString& aBuffer) {
CommandLineParserWin<char> parser; CommandLineParserWin<char> parser;
parser.HandleCommandLine(aBuffer); size_t cch = parser.HandleCommandLine(aBuffer);
++cch; // skip a null char
// Moving |wdpath| to the working dir followed by the first null char.
char* wdpath = aBuffer;
while (*wdpath) {
++wdpath;
}
++wdpath;
nsCOMPtr<nsIFile> workingDir; nsCOMPtr<nsIFile> workingDir;
NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath), false, if (cch < aBuffer.Length()) {
getter_AddRefs(workingDir)); NS_NewLocalFile(NS_ConvertUTF8toUTF16(Substring(aBuffer, cch)), false,
getter_AddRefs(workingDir));
}
mCommandLine = new nsCommandLine(); mCommandLine = new nsCommandLine();
return mCommandLine->Init(parser.Argc(), parser.Argv(), workingDir, return mCommandLine->Init(parser.Argc(), parser.Argv(), workingDir,
nsICommandLine::STATE_REMOTE_AUTO); nsICommandLine::STATE_REMOTE_AUTO);
} }
nsresult WinRemoteMessageReceiver::ParseV2(char16_t* aBuffer) { nsresult WinRemoteMessageReceiver::ParseV2(const nsAString& aBuffer) {
CommandLineParserWin<char16_t> parser; CommandLineParserWin<char16_t> parser;
parser.HandleCommandLine(aBuffer); size_t cch = parser.HandleCommandLine(aBuffer);
++cch; // skip a null char
// Moving |wdpath| to the working dir followed by the first null char.
char16_t* wdpath = aBuffer;
while (*wdpath) {
++wdpath;
}
++wdpath;
nsCOMPtr<nsIFile> workingDir; nsCOMPtr<nsIFile> workingDir;
NS_NewLocalFile(nsDependentString(wdpath), false, getter_AddRefs(workingDir)); if (cch < aBuffer.Length()) {
NS_NewLocalFile(Substring(aBuffer, cch), false, getter_AddRefs(workingDir));
}
int argc = parser.Argc(); int argc = parser.Argc();
Vector<nsAutoCString> utf8args; Vector<nsAutoCString> utf8args;
if (!utf8args.reserve(argc)) { if (!utf8args.reserve(argc)) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
@ -110,14 +101,18 @@ nsresult WinRemoteMessageReceiver::ParseV2(char16_t* aBuffer) {
nsICommandLine::STATE_REMOTE_AUTO); nsICommandLine::STATE_REMOTE_AUTO);
} }
nsresult WinRemoteMessageReceiver::Parse(COPYDATASTRUCT* aMessageData) { nsresult WinRemoteMessageReceiver::Parse(const COPYDATASTRUCT* aMessageData) {
switch (static_cast<WinRemoteMessageVersion>(aMessageData->dwData)) { switch (static_cast<WinRemoteMessageVersion>(aMessageData->dwData)) {
case WinRemoteMessageVersion::CommandLineOnly: case WinRemoteMessageVersion::CommandLineOnly:
return ParseV0(reinterpret_cast<char*>(aMessageData->lpData)); return ParseV0(nsDependentCSubstring(
reinterpret_cast<char*>(aMessageData->lpData), aMessageData->cbData));
case WinRemoteMessageVersion::CommandLineAndWorkingDir: case WinRemoteMessageVersion::CommandLineAndWorkingDir:
return ParseV1(reinterpret_cast<char*>(aMessageData->lpData)); return ParseV1(nsDependentCSubstring(
reinterpret_cast<char*>(aMessageData->lpData), aMessageData->cbData));
case WinRemoteMessageVersion::CommandLineAndWorkingDirInUtf16: case WinRemoteMessageVersion::CommandLineAndWorkingDirInUtf16:
return ParseV2(reinterpret_cast<char16_t*>(aMessageData->lpData)); return ParseV2(nsDependentSubstring(
reinterpret_cast<char16_t*>(aMessageData->lpData),
aMessageData->cbData / sizeof(char16_t)));
default: default:
MOZ_ASSERT_UNREACHABLE("Unsupported message version"); MOZ_ASSERT_UNREACHABLE("Unsupported message version");
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;

View File

@ -51,9 +51,9 @@ class WinRemoteMessageSender final {
class WinRemoteMessageReceiver final { class WinRemoteMessageReceiver final {
nsCOMPtr<nsICommandLineRunner> mCommandLine; nsCOMPtr<nsICommandLineRunner> mCommandLine;
nsresult ParseV0(char* aBuffer); nsresult ParseV0(const nsACString& aBuffer);
nsresult ParseV1(char* aBuffer); nsresult ParseV1(const nsACString& aBuffer);
nsresult ParseV2(char16_t* aBuffer); nsresult ParseV2(const nsAString& aBuffer);
public: public:
WinRemoteMessageReceiver() = default; WinRemoteMessageReceiver() = default;
@ -62,7 +62,7 @@ class WinRemoteMessageReceiver final {
WinRemoteMessageReceiver& operator=(const WinRemoteMessageReceiver&) = delete; WinRemoteMessageReceiver& operator=(const WinRemoteMessageReceiver&) = delete;
WinRemoteMessageReceiver& operator=(WinRemoteMessageReceiver&&) = delete; WinRemoteMessageReceiver& operator=(WinRemoteMessageReceiver&&) = delete;
nsresult Parse(COPYDATASTRUCT* aMessageData); nsresult Parse(const COPYDATASTRUCT* aMessageData);
nsICommandLineRunner* CommandLineRunner(); nsICommandLineRunner* CommandLineRunner();
}; };

View File

@ -444,7 +444,10 @@ inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
// This class converts a command line string into an array of the arguments. // This class converts a command line string into an array of the arguments.
// It's basically the opposite of MakeCommandLine. However, the behavior is // It's basically the opposite of MakeCommandLine. However, the behavior is
// different from ::CommandLineToArgvW in several ways, such as escaping a // different from ::CommandLineToArgvW in several ways, such as escaping a
// backslash or quoting an argument containing whitespaces. // backslash or quoting an argument containing whitespaces. This satisfies
// the examples at:
// https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#results-of-parsing-command-lines
// https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)
template <typename T> template <typename T>
class CommandLineParserWin final { class CommandLineParserWin final {
int mArgc; int mArgc;
@ -472,37 +475,44 @@ class CommandLineParserWin final {
int Argc() const { return mArgc; } int Argc() const { return mArgc; }
const T* const* Argv() const { return mArgv; } const T* const* Argv() const { return mArgv; }
void HandleCommandLine(const T* aCmdLineString) { // Returns the number of characters handled
int HandleCommandLine(const nsTSubstring<T>& aCmdLineString) {
Release(); Release();
if (aCmdLineString.IsEmpty()) {
return 0;
}
int justCounting = 1; int justCounting = 1;
// Flags, etc. // Flags, etc.
int init = 1; int init = 1;
int between, quoted, bSlashCount; int between, quoted, bSlashCount;
const T* p; const T* p;
const T* const pEnd = aCmdLineString.EndReading();
nsTAutoString<T> arg; nsTAutoString<T> arg;
// Parse command line args according to MS spec
// (see "Parsing C++ Command-Line Arguments" at
// http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
// We loop if we've not finished the second pass through. // We loop if we've not finished the second pass through.
while (1) { while (1) {
// Initialize if required. // Initialize if required.
if (init) { if (init) {
p = aCmdLineString; p = aCmdLineString.BeginReading();
between = 1; between = 1;
mArgc = quoted = bSlashCount = 0; mArgc = quoted = bSlashCount = 0;
init = 0; init = 0;
} }
const T charCurr = (p < pEnd) ? *p : 0;
const T charNext = (p + 1 < pEnd) ? *(p + 1) : 0;
if (between) { if (between) {
// We are traversing whitespace between args. // We are traversing whitespace between args.
// Check for start of next arg. // Check for start of next arg.
if (*p != 0 && !wcschr(kCommandLineDelimiter, *p)) { if (charCurr != 0 && !wcschr(kCommandLineDelimiter, charCurr)) {
// Start of another arg. // Start of another arg.
between = 0; between = 0;
arg.Truncate(); arg.Truncate();
switch (*p) { switch (charCurr) {
case '\\': case '\\':
// Count the backslash. // Count the backslash.
bSlashCount = 1; bSlashCount = 1;
@ -513,7 +523,7 @@ class CommandLineParserWin final {
break; break;
default: default:
// Add character to arg. // Add character to arg.
arg += *p; arg += charCurr;
break; break;
} }
} else { } else {
@ -522,7 +532,8 @@ class CommandLineParserWin final {
} else { } else {
// We are processing the contents of an argument. // We are processing the contents of an argument.
// Check for whitespace or end. // Check for whitespace or end.
if (*p == 0 || (!quoted && wcschr(kCommandLineDelimiter, *p))) { if (charCurr == 0 ||
(!quoted && wcschr(kCommandLineDelimiter, charCurr))) {
// Process pending backslashes (interpret them // Process pending backslashes (interpret them
// literally since they're not followed by a "). // literally since they're not followed by a ").
while (bSlashCount) { while (bSlashCount) {
@ -539,7 +550,7 @@ class CommandLineParserWin final {
between = 1; between = 1;
} else { } else {
// Still inside argument, process the character. // Still inside argument, process the character.
switch (*p) { switch (charCurr) {
case '"': case '"':
// First, digest preceding backslashes (if any). // First, digest preceding backslashes (if any).
while (bSlashCount > 1) { while (bSlashCount > 1) {
@ -556,7 +567,7 @@ class CommandLineParserWin final {
if (quoted) { if (quoted) {
// Check for special case of consecutive double // Check for special case of consecutive double
// quotes inside a quoted section. // quotes inside a quoted section.
if (*(p + 1) == '"') { if (charNext == '"') {
// This implies a literal double-quote. Fake that // This implies a literal double-quote. Fake that
// out by causing next double-quote to look as // out by causing next double-quote to look as
// if it was preceded by a backslash. // if it was preceded by a backslash.
@ -580,13 +591,14 @@ class CommandLineParserWin final {
bSlashCount--; bSlashCount--;
} }
// Just add next char to the current arg. // Just add next char to the current arg.
arg += *p; arg += charCurr;
break; break;
} }
} }
} }
// Check for end of input. // Check for end of input.
if (*p) { if (charCurr) {
// Go to next character. // Go to next character.
p++; p++;
} else { } else {
@ -604,6 +616,8 @@ class CommandLineParserWin final {
} }
} }
} }
return p - aCmdLineString.BeginReading();
} }
}; };
# endif // defined(MOZILLA_INTERNAL_API) # endif // defined(MOZILLA_INTERNAL_API)

View File

@ -92,7 +92,7 @@ TEST(CommandLineParserWin, HandleCommandLine)
CommandLineParserWin<char> parser; CommandLineParserWin<char> parser;
for (const auto& testCase : testCases) { for (const auto& testCase : testCases) {
NS_ConvertUTF16toUTF8 utf8(testCase.mExpected); NS_ConvertUTF16toUTF8 utf8(testCase.mExpected);
parser.HandleCommandLine(utf8.get()); parser.HandleCommandLine(utf8);
if (utf8.Length() == 0) { if (utf8.Length() == 0) {
EXPECT_EQ(parser.Argc(), 0); EXPECT_EQ(parser.Argc(), 0);