diff --git a/src/Compilers/Core/CodeAnalysisTest/CommonCommandLineParserTests.cs b/src/Compilers/Core/CodeAnalysisTest/CommonCommandLineParserTests.cs index b54e0b9055c177ceca5f905283b390dcdf8be459..b6487fe20fb701ecabf3850f926c360bad26e075 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CommonCommandLineParserTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/CommonCommandLineParserTests.cs @@ -12,9 +12,9 @@ public class CommonCommandLineParserTests : TestBase { private const int EN_US = 1033; - private void VerifyCommandLineSplitter(string commandLine, string[] expected) + private void VerifyCommandLineSplitter(string commandLine, string[] expected, bool removeHashComments = false) { - string[] actual = CommandLineSplitter.SplitCommandLine(commandLine); + var actual = CommandLineParser.SplitCommandLineIntoArguments(commandLine, removeHashComments).ToArray(); Assert.Equal(expected.Length, actual.Length); for (int i = 0; i < actual.Length; ++i) @@ -94,6 +94,8 @@ public void TestCommandLineSplitter() new string[] { @"\""abc def""", @"\""abc", @"def""" }); VerifyCommandLineSplitter(@" \\\\""abc def"" \\\\\""abc def"" ", new string[] { @"\\""abc def""", @"\\""abc", @"def""" }); + VerifyCommandLineSplitter(@"abc #Comment ignored", + new string[] { @"abc" }, removeHashComments: true); } [Fact] diff --git a/src/Compilers/Core/Desktop/CommandLine/CommonCommandLineParser.cs b/src/Compilers/Core/Desktop/CommandLine/CommonCommandLineParser.cs index 21eea59147f3f09ded13b7c47b7a8eb58cb92a40..940d4969df01461461fdf96c229e5f2f62843796 100644 --- a/src/Compilers/Core/Desktop/CommandLine/CommonCommandLineParser.cs +++ b/src/Compilers/Core/Desktop/CommandLine/CommonCommandLineParser.cs @@ -7,7 +7,7 @@ using System.IO; using System.Linq; using System.Text; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -509,15 +509,20 @@ private static IEnumerable Split(string str, Func splitHere) /// private static string CondenseDoubledBackslashes(string input) { + int i = input.IndexOf('\\'); + // Simple case -- no backslashes. - if (!input.Contains('\\')) + if (i < 0) return input; - StringBuilder builder = new StringBuilder(); - int backslashCount = 0; + var pooledBuilder = PooledStringBuilder.GetInstance(); + var builder = pooledBuilder.Builder; + builder.Append(input, 0, i); - foreach (char c in input) + int backslashCount = 0; + do { + char c = input[i]; if (c == '\\') { ++backslashCount; @@ -537,10 +542,10 @@ private static string CondenseDoubledBackslashes(string input) builder.Append(c); backslashCount = 0; } - } + } while (++i < input.Length); AddBackslashes(builder, backslashCount); - return builder.ToString(); + return pooledBuilder.ToStringAndFree(); } /// @@ -865,153 +870,4 @@ internal static bool TryParseUInt16(string value, out ushort result) return true; } } - - /// - /// Rules for command line parsing, according to MSDN: - /// - /// Arguments are delimited by white space, which is either a space or a tab. - /// - /// A string surrounded by double quotation marks ("string") is interpreted - /// as a single argument, regardless of white space contained within. - /// A quoted string can be embedded in an argument. - /// - /// A double quotation mark preceded by a backslash (\") is interpreted as a - /// literal double quotation mark character ("). - /// - /// Backslashes are interpreted literally, unless they immediately precede a - /// double quotation mark. - /// - /// If an even number of backslashes is followed by a double quotation mark, - /// one backslash is placed in the argv array for every pair of backslashes, - /// and the double quotation mark is interpreted as a string delimiter. - /// - /// If an odd number of backslashes is followed by a double quotation mark, - /// one backslash is placed in the argv array for every pair of backslashes, - /// and the double quotation mark is "escaped" by the remaining backslash, - /// causing a literal double quotation mark (") to be placed in argv. - /// - internal static class CommandLineSplitter - { - public static bool IsDelimiter(char c) - { - return c == ' ' || c == '\t'; - } - - public static bool IsQuote(char c) - { - // Only double quotes are respected, according to MSDN. - return c == '\"'; - } - - private const char Backslash = '\\'; - - // Split a command line by the same rules as Main would get the commands. - public static string[] SplitCommandLine(string commandLine) - { - bool inQuotes = false; - int backslashCount = 0; - - return commandLine.Split(c => - { - if (c == Backslash) - { - backslashCount += 1; - } - else if (IsQuote(c)) - { - if ((backslashCount & 1) != 1) - { - inQuotes = !inQuotes; - } - backslashCount = 0; - } - else - { - backslashCount = 0; - } - - return !inQuotes && IsDelimiter(c); - }) - .Select(arg => arg.Trim().CondenseDoubledBackslashes().TrimMatchingQuotes()) - .Where(arg => !string.IsNullOrEmpty(arg)) - .ToArray(); - } - - // Split a string, based on whether "splitHere" returned true on each character. - private static IEnumerable Split(this string str, - Func splitHere) - { - int nextPiece = 0; - - for (int c = 0; c < str.Length; c++) - { - if (splitHere(str[c])) - { - yield return str.Substring(nextPiece, c - nextPiece); - nextPiece = c + 1; - } - } - - yield return str.Substring(nextPiece); - } - - // Trim leading and trailing quotes from a string, if they are there. Only trims - // one pair. - private static string TrimMatchingQuotes(this string input) - { - if ((input.Length >= 2) && - (IsQuote(input[0])) && - (IsQuote(input[input.Length - 1]))) - { - return input.Substring(1, input.Length - 2); - } - else - { - return input; - } - } - - // Condense double backslashes that precede a quotation mark to single backslashes. - private static string CondenseDoubledBackslashes(this string input) - { - // Simple case -- no backslashes. - if (!input.Contains(Backslash)) - return input; - - StringBuilder builder = new StringBuilder(); - int doubleQuoteCount = 0; - - foreach (char c in input) - { - if (c == Backslash) - { - ++doubleQuoteCount; - } - else - { - // Add right amount of pending backslashes. - if (IsQuote(c)) - { - AddBackslashes(builder, doubleQuoteCount / 2); - } - else - { - AddBackslashes(builder, doubleQuoteCount); - } - - builder.Append(c); - doubleQuoteCount = 0; - } - } - - AddBackslashes(builder, doubleQuoteCount); - return builder.ToString(); - } - - private static void AddBackslashes(StringBuilder builder, int count) - { - for (int i = 0; i < count; ++i) - builder.Append(Backslash); - } - } }