/Source/Hanhud/Utils/CWindowsCommandLineHelper.cs

# · C# · 106 lines · 80 code · 6 blank · 20 comment · 7 complexity · 5dc679e5487d054b0202f9df3f5fceae MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace Hanhud.Utils
  5. {
  6. /// <summary>
  7. /// Provides shared helper functionality for working with Windows process command-lines.
  8. /// </summary>
  9. public static class CWindowsCommandLineHelper
  10. {
  11. /// <summary>
  12. /// Performs escaping and quoting of arguments where necessary to
  13. /// build up a command-line suitable for use with the
  14. /// <see cref="Process.Start" /> method.
  15. /// </summary>
  16. /// <param name="arguments">The arguments to be included on the command-line.</param>
  17. /// <returns>The resulting command-line.</returns>
  18. public static string FormatCommandLine(params string[] arguments)
  19. {
  20. arguments = (string[])arguments.Clone();
  21. for (int index = 0; index < arguments.Length; index++)
  22. {
  23. arguments[index] = GetQuotedArgument(arguments[index]);
  24. }
  25. return string.Join(" ", arguments);
  26. }
  27. private static string GetQuotedArgument(string argument)
  28. {
  29. // The method reads the input argument backwards and builds
  30. // the result in reverse-character order.
  31. StringBuilder resultBuilder = new StringBuilder();
  32. // If the text has whitespace, it must be surrounded in quotes.
  33. bool surroundingQuotesRequired = HasWhitespace(argument);
  34. // This flag tracks whether the last character processed was a
  35. // quote or a backslash that belongs to a sequence of backslashes
  36. // that immediately precede a quote.
  37. bool precedingQuote = false;
  38. // If surrounding quotes are required, start with one.
  39. // This means that any slashes at the end must be doubled up,
  40. // so we must also set the precedingQuote flag.
  41. if (surroundingQuotesRequired)
  42. {
  43. resultBuilder.Append('"');
  44. precedingQuote = true;
  45. }
  46. // Read the argument string backwards, escaping any quotes and backslashes
  47. // where necessary (backslashes are OK unless in a sequence preceding a quote).
  48. for (int index = argument.Length - 1; index >= 0; index--)
  49. {
  50. char character = argument[index];
  51. resultBuilder.Append(character);
  52. if (character == '"')
  53. {
  54. precedingQuote = true;
  55. resultBuilder.Append('\\');
  56. }
  57. else if (character == '\\' && precedingQuote)
  58. {
  59. resultBuilder.Append('\\');
  60. }
  61. else
  62. {
  63. precedingQuote = false;
  64. }
  65. }
  66. // If surrounding quotes are required, add one.
  67. if (surroundingQuotesRequired)
  68. {
  69. resultBuilder.Append('"');
  70. }
  71. // Reverse the result and we're done.
  72. return Reverse(resultBuilder.ToString());
  73. }
  74. private static bool HasWhitespace(string argument)
  75. {
  76. // Iterate over the string and return true if it contains any whitespace characters.
  77. foreach (char character in argument)
  78. {
  79. if (char.IsWhiteSpace(character))
  80. {
  81. return true;
  82. }
  83. }
  84. return false;
  85. }
  86. private static string Reverse(string input)
  87. {
  88. // Convert the string to a character array, reverse the order
  89. // and convert it back.
  90. char[] array = input.ToCharArray();
  91. Array.Reverse(array);
  92. return new string(array);
  93. }
  94. }
  95. }