using System.Globalization;
using System.Text;
namespace Geekeey.Extensions.Process;
///
/// Builder that helps format command-line arguments into a string.
///
public sealed partial class ArgumentsBuilder
{
private static readonly IFormatProvider DefaultFormatProvider = CultureInfo.InvariantCulture;
private readonly StringBuilder _buffer = new();
///
/// Adds the specified value to the list of arguments.
///
public ArgumentsBuilder Add(string value, bool escape = true)
{
if (_buffer.Length > 0)
_buffer.Append(' ');
_buffer.Append(escape ? Escape(value) : value);
return this;
}
///
/// Adds the specified values to the list of arguments.
///
public ArgumentsBuilder Add(IEnumerable values, bool escape = true)
{
foreach (var value in values)
{
Add(value, escape);
}
return this;
}
///
/// Adds the specified value to the list of arguments.
///
public ArgumentsBuilder Add(IFormattable value, IFormatProvider formatProvider, bool escape = true)
=> Add(value.ToString(null, formatProvider), escape);
///
/// Adds the specified value to the list of arguments.
/// The value is converted to string using invariant culture.
///
public ArgumentsBuilder Add(IFormattable value, bool escape = true)
=> Add(value, DefaultFormatProvider, escape);
///
/// Adds the specified values to the list of arguments.
///
public ArgumentsBuilder Add(IEnumerable values, IFormatProvider formatProvider, bool escape = true)
{
foreach (var value in values)
{
Add(value, formatProvider, escape);
}
return this;
}
///
/// Adds the specified values to the list of arguments.
/// The values are converted to string using invariant culture.
///
public ArgumentsBuilder Add(IEnumerable values, bool escape = true)
=> Add(values, DefaultFormatProvider, escape);
///
/// Builds the resulting arguments string.
///
public string Build() => _buffer.ToString();
}
public partial class ArgumentsBuilder
{
private static string Escape(string argument)
{
// Short circuit if the argument is clean and doesn't need escaping
if (argument.Length > 0 && argument.All(c => !char.IsWhiteSpace(c) && c is not '"'))
return argument;
var buffer = new StringBuilder();
buffer.Append('"');
for (var i = 0; i < argument.Length;)
{
var c = argument[i++];
switch (c)
{
case '\\':
{
var backslashCount = 1;
while (i < argument.Length && argument[i] == '\\')
{
backslashCount++;
i++;
}
if (i == argument.Length)
{
buffer.Append('\\', backslashCount * 2);
}
else if (argument[i] == '"')
{
buffer.Append('\\', backslashCount * 2 + 1).Append('"');
i++;
}
else
{
buffer.Append('\\', backslashCount);
}
break;
}
case '"':
buffer.Append('\\').Append('"');
break;
default:
buffer.Append(c);
break;
}
}
buffer.Append('"');
return buffer.ToString();
}
}