PasteArguments.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Text;
  5. namespace System
  6. {
  7. internal static partial class PasteArguments
  8. {
  9. internal static void AppendArgument(StringBuilder stringBuilder, string argument)
  10. {
  11. if (stringBuilder.Length != 0)
  12. {
  13. stringBuilder.Append(' ');
  14. }
  15. // Parsing rules for non-argv[0] arguments:
  16. // - Backslash is a normal character except followed by a quote.
  17. // - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote
  18. // - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote
  19. // - Parsing stops at first whitespace outside of quoted region.
  20. // - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode.
  21. if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument))
  22. {
  23. // Simple case - no quoting or changes needed.
  24. stringBuilder.Append(argument);
  25. }
  26. else
  27. {
  28. stringBuilder.Append(Quote);
  29. int idx = 0;
  30. while (idx < argument.Length)
  31. {
  32. char c = argument[idx++];
  33. if (c == Backslash)
  34. {
  35. int numBackSlash = 1;
  36. while (idx < argument.Length && argument[idx] == Backslash)
  37. {
  38. idx++;
  39. numBackSlash++;
  40. }
  41. if (idx == argument.Length)
  42. {
  43. // We'll emit an end quote after this so must double the number of backslashes.
  44. stringBuilder.Append(Backslash, numBackSlash * 2);
  45. }
  46. else if (argument[idx] == Quote)
  47. {
  48. // Backslashes will be followed by a quote. Must double the number of backslashes.
  49. stringBuilder.Append(Backslash, numBackSlash * 2 + 1);
  50. stringBuilder.Append(Quote);
  51. idx++;
  52. }
  53. else
  54. {
  55. // Backslash will not be followed by a quote, so emit as normal characters.
  56. stringBuilder.Append(Backslash, numBackSlash);
  57. }
  58. continue;
  59. }
  60. if (c == Quote)
  61. {
  62. // Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed
  63. // by another quote (which parses differently pre-2008 vs. post-2008.)
  64. stringBuilder.Append(Backslash);
  65. stringBuilder.Append(Quote);
  66. continue;
  67. }
  68. stringBuilder.Append(c);
  69. }
  70. stringBuilder.Append(Quote);
  71. }
  72. }
  73. private static bool ContainsNoWhitespaceOrQuotes(string s)
  74. {
  75. for (int i = 0; i < s.Length; i++)
  76. {
  77. char c = s[i];
  78. if (char.IsWhiteSpace(c) || c == Quote)
  79. {
  80. return false;
  81. }
  82. }
  83. return true;
  84. }
  85. private const char Quote = '\"';
  86. private const char Backslash = '\\';
  87. }
  88. }