// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Text; namespace System { internal static partial class PasteArguments { internal static void AppendArgument(StringBuilder stringBuilder, string argument) { if (stringBuilder.Length != 0) { stringBuilder.Append(' '); } // Parsing rules for non-argv[0] arguments: // - Backslash is a normal character except followed by a quote. // - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote // - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote // - Parsing stops at first whitespace outside of quoted region. // - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode. if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument)) { // Simple case - no quoting or changes needed. stringBuilder.Append(argument); } else { stringBuilder.Append(Quote); int idx = 0; while (idx < argument.Length) { char c = argument[idx++]; if (c == Backslash) { int numBackSlash = 1; while (idx < argument.Length && argument[idx] == Backslash) { idx++; numBackSlash++; } if (idx == argument.Length) { // We'll emit an end quote after this so must double the number of backslashes. stringBuilder.Append(Backslash, numBackSlash * 2); } else if (argument[idx] == Quote) { // Backslashes will be followed by a quote. Must double the number of backslashes. stringBuilder.Append(Backslash, numBackSlash * 2 + 1); stringBuilder.Append(Quote); idx++; } else { // Backslash will not be followed by a quote, so emit as normal characters. stringBuilder.Append(Backslash, numBackSlash); } continue; } if (c == Quote) { // Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed // by another quote (which parses differently pre-2008 vs. post-2008.) stringBuilder.Append(Backslash); stringBuilder.Append(Quote); continue; } stringBuilder.Append(c); } stringBuilder.Append(Quote); } } private static bool ContainsNoWhitespaceOrQuotes(string s) { for (int i = 0; i < s.Length; i++) { char c = s[i]; if (char.IsWhiteSpace(c) || c == Quote) { return false; } } return true; } private const char Quote = '\"'; private const char Backslash = '\\'; } }