mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Replace native interop CommandLineToArgvW with parsing in Process.Unix.cs from System.Diagnostics.Processpull/3203/head
7 changed files with 228 additions and 118 deletions
@ -0,0 +1,220 @@
@@ -0,0 +1,220 @@
|
||||
// Copyright (c) 2024 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Text; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
public class CommandLineTools |
||||
{ |
||||
/// <summary>
|
||||
/// Decodes a command line into an array of arguments according to the CommandLineToArgvW rules.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Command line parsing rules:
|
||||
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
|
||||
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
|
||||
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
|
||||
/// </remarks>
|
||||
public static string[] CommandLineToArgumentArray(string commandLine) |
||||
{ |
||||
if (string.IsNullOrEmpty(commandLine)) |
||||
return Array.Empty<string>(); |
||||
|
||||
var results = new List<string>(); |
||||
ParseArgv.ParseArgumentsIntoList(commandLine, results); |
||||
|
||||
return results.ToArray(); |
||||
} |
||||
|
||||
static readonly char[] charsNeedingQuoting = { ' ', '\t', '\n', '\v', '"' }; |
||||
|
||||
/// <summary>
|
||||
/// Escapes a set of arguments according to the CommandLineToArgvW rules.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Command line parsing rules:
|
||||
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
|
||||
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
|
||||
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
|
||||
/// </remarks>
|
||||
public static string ArgumentArrayToCommandLine(params string[] arguments) |
||||
{ |
||||
if (arguments == null) |
||||
return null; |
||||
StringBuilder b = new StringBuilder(); |
||||
for (int i = 0; i < arguments.Length; i++) |
||||
{ |
||||
if (i > 0) |
||||
b.Append(' '); |
||||
AppendArgument(b, arguments[i]); |
||||
} |
||||
return b.ToString(); |
||||
} |
||||
|
||||
static void AppendArgument(StringBuilder b, string arg) |
||||
{ |
||||
if (arg == null) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0) |
||||
{ |
||||
b.Append(arg); |
||||
} |
||||
else |
||||
{ |
||||
b.Append('"'); |
||||
for (int j = 0; ; j++) |
||||
{ |
||||
int backslashCount = 0; |
||||
while (j < arg.Length && arg[j] == '\\') |
||||
{ |
||||
backslashCount++; |
||||
j++; |
||||
} |
||||
if (j == arg.Length) |
||||
{ |
||||
b.Append('\\', backslashCount * 2); |
||||
break; |
||||
} |
||||
else if (arg[j] == '"') |
||||
{ |
||||
b.Append('\\', backslashCount * 2 + 1); |
||||
b.Append('"'); |
||||
} |
||||
else |
||||
{ |
||||
b.Append('\\', backslashCount); |
||||
b.Append(arg[j]); |
||||
} |
||||
} |
||||
b.Append('"'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Source: https://github.com/dotnet/runtime/blob/bc9fc5a774d96f95abe0ea5c90fac48b38ed2e67/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L574-L606
|
||||
// Minor adaptations in GetNextArgument (ValueStringBuilder replaced), otherwise kept as identical as possible
|
||||
public static class ParseArgv |
||||
{ |
||||
/// <summary>Parses a command-line argument string into a list of arguments.</summary>
|
||||
/// <param name="arguments">The argument string.</param>
|
||||
/// <param name="results">The list into which the component arguments should be stored.</param>
|
||||
/// <remarks>
|
||||
/// This follows the rules outlined in "Parsing C++ Command-Line Arguments" at
|
||||
/// https://msdn.microsoft.com/en-us/library/17w5ykft.aspx.
|
||||
/// </remarks>
|
||||
public static void ParseArgumentsIntoList(string arguments, List<string> results) |
||||
{ |
||||
// Iterate through all of the characters in the argument string.
|
||||
for (int i = 0; i < arguments.Length; i++) |
||||
{ |
||||
while (i < arguments.Length && (arguments[i] == ' ' || arguments[i] == '\t')) |
||||
i++; |
||||
|
||||
if (i == arguments.Length) |
||||
break; |
||||
|
||||
results.Add(GetNextArgument(arguments, ref i)); |
||||
} |
||||
} |
||||
|
||||
private static string GetNextArgument(string arguments, ref int i) |
||||
{ |
||||
var currentArgument = new StringBuilder(); |
||||
bool inQuotes = false; |
||||
|
||||
while (i < arguments.Length) |
||||
{ |
||||
// From the current position, iterate through contiguous backslashes.
|
||||
int backslashCount = 0; |
||||
while (i < arguments.Length && arguments[i] == '\\') |
||||
{ |
||||
i++; |
||||
backslashCount++; |
||||
} |
||||
|
||||
if (backslashCount > 0) |
||||
{ |
||||
if (i >= arguments.Length || arguments[i] != '"') |
||||
{ |
||||
// Backslashes not followed by a double quote:
|
||||
// they should all be treated as literal backslashes.
|
||||
currentArgument.Append('\\', backslashCount); |
||||
} |
||||
else |
||||
{ |
||||
// Backslashes followed by a double quote:
|
||||
// - Output a literal slash for each complete pair of slashes
|
||||
// - If one remains, use it to make the subsequent quote a literal.
|
||||
currentArgument.Append('\\', backslashCount / 2); |
||||
if (backslashCount % 2 != 0) |
||||
{ |
||||
currentArgument.Append('"'); |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
continue; |
||||
} |
||||
|
||||
char c = arguments[i]; |
||||
|
||||
// If this is a double quote, track whether we're inside of quotes or not.
|
||||
// Anything within quotes will be treated as a single argument, even if
|
||||
// it contains spaces.
|
||||
if (c == '"') |
||||
{ |
||||
if (inQuotes && i < arguments.Length - 1 && arguments[i + 1] == '"') |
||||
{ |
||||
// Two consecutive double quotes inside an inQuotes region should result in a literal double quote
|
||||
// (the parser is left in the inQuotes region).
|
||||
// This behavior is not part of the spec of code:ParseArgumentsIntoList, but is compatible with CRT
|
||||
// and .NET Framework.
|
||||
currentArgument.Append('"'); |
||||
i++; |
||||
} |
||||
else |
||||
{ |
||||
inQuotes = !inQuotes; |
||||
} |
||||
|
||||
i++; |
||||
continue; |
||||
} |
||||
|
||||
// If this is a space/tab and we're not in quotes, we're done with the current
|
||||
// argument, it should be added to the results and then reset for the next one.
|
||||
if ((c == ' ' || c == '\t') && !inQuotes) |
||||
{ |
||||
break; |
||||
} |
||||
|
||||
// Nothing special; add the character to the current argument.
|
||||
currentArgument.Append(c); |
||||
i++; |
||||
} |
||||
|
||||
return currentArgument.ToString(); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue