| File: ArgumentEscaper.cs | Web Access |
| Project: src\arcade\src\Common\Microsoft.Arcade.Common\Microsoft.Arcade.Common.csproj (Microsoft.Arcade.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Arcade.Common { public static class ArgumentEscaper { /// <summary> /// Escapes and quote arguments that need it (contain a space, a quote...). /// </summary> public static string EscapeAndConcatenateArgArrayForProcessStart(IEnumerable<string> args) { return string.Join(" ", args.Select(EscapeArg)); } /// <summary> /// Escapes and quote arguments that need it. /// /// This prefixes every character with the '^' character to force cmd to /// interpret the argument string literally. An alternative option would /// be to do this only for cmd metacharacters. /// /// See here for more info: /// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx /// </summary> public static string EscapeAndConcatenateArgArrayForCmdProcessStart(IEnumerable<string> args) { return string.Join(" ", args.Select(EscapeArgForCmd)); } private static string EscapeArg(string argument) { var sb = new StringBuilder(); // Don't quote already quoted strings bool quoted = IsQuoted(argument); var shouldQuote = !quoted && ShouldSurroundWithQuotes(argument); if (shouldQuote || quoted) { sb.Append("\""); } for (int i = 0; i < argument.Length; ++i) { if (quoted && (i == 0 || i == argument.Length - 1)) { continue; } var backslashCount = 0; // Consume All Backslashes while (i < argument.Length && argument[i] == '\\') { backslashCount++; i++; } // Escape any backslashes at the end of the arg // This ensures the outside quote is interpreted as // an argument delimiter if (i == argument.Length) { sb.Append('\\', 2 * backslashCount); } // Escape any preceding backslashes and the quote else if (argument[i] == '"') { sb.Append('\\', (2 * backslashCount) + 1); sb.Append('"'); } // Output any consumed backslashes and the character else { sb.Append('\\', backslashCount); sb.Append(argument[i]); } } if (shouldQuote || quoted) { sb.Append("\""); } return sb.ToString(); } private static string EscapeArgForCmd(string argument) { var sb = new StringBuilder(); var quoted = IsQuoted(argument); var shouldQuote = !quoted && ShouldSurroundWithQuotes(argument); if (shouldQuote) { sb.Append("^\""); } foreach (var character in argument) { if (character == '"') { sb.Append('^'); sb.Append('"'); sb.Append('^'); sb.Append(character); } else { sb.Append("^"); sb.Append(character); } } if (shouldQuote) { sb.Append("^\""); } return sb.ToString(); } private static bool ShouldSurroundWithQuotes(string argument) { // Only quote if whitespace exists in the string return argument.Contains(' ') || argument.Contains('\t') || argument.Contains('\n') || argument.Contains('"'); } private static bool IsQuoted(string argument) { return argument.Length > 1 && (argument[0] == '\"' || argument[argument.Length - 1] == '\"'); } } }