// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Tools.DotNETCommon
{
///
/// Helper class to visualize an argument list
///
class CommandLineArgumentListView
{
///
/// The list of arguments
///
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public string[] Arguments;
///
/// Constructor
///
/// The argument list to proxy
public CommandLineArgumentListView(CommandLineArguments ArgumentList)
{
Arguments = ArgumentList.GetRawArray();
}
}
///
/// Exception thrown for invalid command line arguments
///
public class CommandLineArgumentException : Exception
{
///
/// Constructor
///
/// Message to display for this exception
public CommandLineArgumentException(string Message)
: base(Message)
{
}
///
/// Constructor
///
/// Message to display for this exception
/// The inner exception
public CommandLineArgumentException(string Message, Exception InnerException)
: base(Message, InnerException)
{
}
///
/// Converts this exception to a string
///
/// Exception message
public override string ToString()
{
return Message;
}
}
///
/// Stores a list of command line arguments, allowing efficient ad-hoc queries of particular options (eg. "-Flag") and retreival of typed values (eg. "-Foo=Bar"), as
/// well as attribute-driven application to fields with the [CommandLine] attribute applied.
///
/// Also tracks which arguments have been retrieved, allowing the display of diagnostic messages for invalid arguments.
///
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(CommandLineArgumentListView))]
public class CommandLineArguments : IReadOnlyList, IReadOnlyCollection, IEnumerable, IEnumerable
{
///
/// The raw array of arguments
///
string[] Arguments;
///
/// Bitmask indicating which arguments are flags rather than values
///
BitArray FlagArguments;
///
/// Bitmask indicating which arguments have been used, via calls to GetOption(), GetValues() etc...
///
BitArray UsedArguments;
///
/// Dictionary of argument names (or prefixes, in the case of "-Foo=" style arguments) to their index into the arguments array.
///
Dictionary ArgumentToFirstIndex;
///
/// For each argument which is seen more than once, keeps a list of indices for the second and subsequent arguments.
///
int[] NextArgumentIndex;
///
/// Array of characters that separate argument names from values
///
static readonly char[] ValueSeparators = { '=', ':' };
///
/// Constructor
///
/// The raw list of arguments
public CommandLineArguments(string[] Arguments)
{
this.Arguments = Arguments;
this.FlagArguments = new BitArray(Arguments.Length);
this.UsedArguments = new BitArray(Arguments.Length);
// Clear the linked list of identical arguments
NextArgumentIndex = new int[Arguments.Length];
for(int Idx = 0; Idx < Arguments.Length; Idx++)
{
NextArgumentIndex[Idx] = -1;
}
// Temporarily store the index of the last matching argument
int[] LastArgumentIndex = new int[Arguments.Length];
// Parse the argument array and build a lookup
ArgumentToFirstIndex = new Dictionary(Arguments.Length, StringComparer.OrdinalIgnoreCase);
for(int Idx = 0; Idx < Arguments.Length; Idx++)
{
int SeparatorIdx = Arguments[Idx].IndexOfAny(ValueSeparators);
if(SeparatorIdx == -1)
{
// Ignore duplicate -Option flags; they are harmless.
if(ArgumentToFirstIndex.ContainsKey(Arguments[Idx]))
{
UsedArguments.Set(Idx, true);
}
else
{
ArgumentToFirstIndex.Add(Arguments[Idx], Idx);
}
// Mark this argument as a flag
FlagArguments.Set(Idx, true);
}
else
{
// Just take the part up to and including the separator character
string Prefix = Arguments[Idx].Substring(0, SeparatorIdx + 1);
// Add the prefix to the argument lookup, or update the appropriate matching argument list if it's been seen before
int ExistingArgumentIndex;
if(ArgumentToFirstIndex.TryGetValue(Prefix, out ExistingArgumentIndex))
{
NextArgumentIndex[LastArgumentIndex[ExistingArgumentIndex]] = Idx;
LastArgumentIndex[ExistingArgumentIndex] = Idx;
}
else
{
ArgumentToFirstIndex.Add(Prefix, Idx);
LastArgumentIndex[Idx] = Idx;
}
}
}
}
///
/// The number of arguments in this list
///
public int Count
{
get { return Arguments.Length; }
}
///
/// Access an argument by index
///
/// Index of the argument
/// The argument at the given index
public string this[int Index]
{
get { return Arguments[Index]; }
}
///
/// Determines if an argument has been used
///
/// Index of the argument
/// True if the argument has been used, false otherwise
public bool HasBeenUsed(int Index)
{
return UsedArguments.Get(Index);
}
///
/// Marks an argument as having been used
///
/// Index of the argument to mark as used
public void MarkAsUsed(int Index)
{
UsedArguments.Set(Index, true);
}
///
/// Marks an argument as not having been used
///
/// Index of the argument to mark as being unused
public void MarkAsUnused(int Index)
{
UsedArguments.Set(Index, true);
}
///
/// Checks if the given option (eg. "-Foo") was specified on the command line.
///
/// The option to look for
/// True if the option was found, false otherwise.
public bool HasOption(string Option)
{
int Index;
if(ArgumentToFirstIndex.TryGetValue(Option, out Index))
{
UsedArguments.Set(Index, true);
return true;
}
return false;
}
///
/// Checks for an argument prefixed with the given string is present.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// True if an argument with the given prefix was specified
public bool HasValue(string Prefix)
{
CheckValidPrefix(Prefix);
return ArgumentToFirstIndex.ContainsKey(Prefix);
}
///
/// Gets the value specified by an argument with the given prefix. Throws an exception if the argument was not specified.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument
public string GetString(string Prefix)
{
string Value;
if(!TryGetValue(Prefix, out Value))
{
throw new CommandLineArgumentException(String.Format("Missing '{0}...' argument", Prefix));
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix. Throws an exception if the argument was not specified.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument
public int GetInteger(string Prefix)
{
int Value;
if(!TryGetValue(Prefix, out Value))
{
throw new CommandLineArgumentException(String.Format("Missing '{0}...' argument", Prefix));
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix. Throws an exception if the argument was not specified.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument
public FileReference GetFileReference(string Prefix)
{
FileReference Value;
if(!TryGetValue(Prefix, out Value))
{
throw new CommandLineArgumentException(String.Format("Missing '{0}...' argument", Prefix));
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix. Throws an exception if the argument was not specified.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument
public DirectoryReference GetDirectoryReference(string Prefix)
{
DirectoryReference Value;
if(!TryGetValue(Prefix, out Value))
{
throw new CommandLineArgumentException(String.Format("Missing '{0}...' argument", Prefix));
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix. Throws an exception if the argument was not specified.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument
public T GetEnum(string Prefix) where T : struct
{
T Value;
if(!TryGetValue(Prefix, out Value))
{
throw new CommandLineArgumentException(String.Format("Missing '{0}...' argument", Prefix));
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix, or a default value.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Default value for the argument
/// Value of the argument
public string GetStringOrDefault(string Prefix, string DefaultValue)
{
string Value;
if(!TryGetValue(Prefix, out Value))
{
Value = DefaultValue;
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix, or a default value.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Default value for the argument
/// Value of the argument
public int GetIntegerOrDefault(string Prefix, int DefaultValue)
{
int Value;
if(!TryGetValue(Prefix, out Value))
{
Value = DefaultValue;
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix, or a default value.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Default value for the argument
/// Value of the argument
public FileReference GetFileReferenceOrDefault(string Prefix, FileReference DefaultValue)
{
FileReference Value;
if(!TryGetValue(Prefix, out Value))
{
Value = DefaultValue;
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix, or a default value.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Default value for the argument
/// Value of the argument
public DirectoryReference GetDirectoryReferenceOrDefault(string Prefix, DirectoryReference DefaultValue)
{
DirectoryReference Value;
if(!TryGetValue(Prefix, out Value))
{
Value = DefaultValue;
}
return Value;
}
///
/// Gets the value specified by an argument with the given prefix, or a default value.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Default value for the argument
/// Value of the argument
public T GetEnumOrDefault(string Prefix, T DefaultValue) where T : struct
{
T Value;
if(!TryGetValue(Prefix, out Value))
{
Value = DefaultValue;
}
return Value;
}
///
/// Tries to gets the value specified by an argument with the given prefix.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument, if found
/// True if the argument was found (and Value was set), false otherwise.
public bool TryGetValue(string Prefix, out string Value)
{
CheckValidPrefix(Prefix);
int Index;
if(!ArgumentToFirstIndex.TryGetValue(Prefix, out Index))
{
Value = null;
return false;
}
if(NextArgumentIndex[Index] != -1)
{
throw new CommandLineArgumentException(String.Format("Multiple {0}... arguments are specified", Prefix));
}
UsedArguments.Set(Index, true);
Value = Arguments[Index].Substring(Prefix.Length);
return true;
}
///
/// Tries to gets the value specified by an argument with the given prefix.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument, if found
/// True if the argument was found (and Value was set), false otherwise.
public bool TryGetValue(string Prefix, out int Value)
{
// Try to get the string value of this argument
string StringValue;
if(!TryGetValue(Prefix, out StringValue))
{
Value = 0;
return false;
}
// Try to parse it. If it fails, throw an exception.
try
{
Value = int.Parse(StringValue);
return true;
}
catch(Exception Ex)
{
throw new CommandLineArgumentException(String.Format("The argument '{0}{1}' does not specify a valid integer", Prefix, StringValue), Ex);
}
}
///
/// Tries to gets the value specified by an argument with the given prefix.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument, if found
/// True if the argument was found (and Value was set), false otherwise.
public bool TryGetValue(string Prefix, out FileReference Value)
{
// Try to get the string value of this argument
string StringValue;
if(!TryGetValue(Prefix, out StringValue))
{
Value = null;
return false;
}
// Try to parse it. If it fails, throw an exception.
try
{
Value = new FileReference(StringValue);
return true;
}
catch(Exception Ex)
{
throw new CommandLineArgumentException(String.Format("The argument '{0}{1}' does not specify a valid file name", Prefix, StringValue), Ex);
}
}
///
/// Tries to gets the value specified by an argument with the given prefix.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument, if found
/// True if the argument was found (and Value was set), false otherwise.
public bool TryGetValue(string Prefix, out DirectoryReference Value)
{
// Try to get the string value of this argument
string StringValue;
if(!TryGetValue(Prefix, out StringValue))
{
Value = null;
return false;
}
// Try to parse it. If it fails, throw an exception.
try
{
Value = new DirectoryReference(StringValue);
return true;
}
catch(Exception Ex)
{
throw new CommandLineArgumentException(String.Format("The argument '{0}{1}' does not specify a valid directory name", Prefix, StringValue), Ex);
}
}
///
/// Tries to gets the value specified by an argument with the given prefix.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Value of the argument, if found
/// True if the argument was found (and Value was set), false otherwise.
public bool TryGetValue(string Prefix, out T Value) where T : struct
{
// Try to get the string value of this argument
string StringValue;
if(!TryGetValue(Prefix, out StringValue))
{
Value = new T();
return false;
}
// Try to parse it. If it fails, throw an exception.
try
{
Value = (T)Enum.Parse(typeof(T), StringValue, true);
return true;
}
catch(Exception Ex)
{
throw new CommandLineArgumentException(String.Format("The argument '{0}{1}' does not specify a valid {2}", Prefix, StringValue, typeof(T).Name), Ex);
}
}
///
/// Returns all arguments with the given prefix.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// Sequence of values for the given prefix.
public IEnumerable GetValues(string Prefix)
{
CheckValidPrefix(Prefix);
int Index;
if(ArgumentToFirstIndex.TryGetValue(Prefix, out Index))
{
for(; Index != -1; Index = NextArgumentIndex[Index])
{
UsedArguments.Set(Index, true);
yield return Arguments[Index].Substring(Prefix.Length);
}
}
}
///
/// Returns all arguments with the given prefix, allowing multiple arguments to be specified in a single argument with a separator character.
///
/// The argument prefix (eg. "-Foo="). Must end with an '=' character.
/// The separator character (eg. '+')
/// Sequence of values for the given prefix.
public IEnumerable GetValues(string Prefix, char Separator)
{
foreach(string Value in GetValues(Prefix))
{
foreach(string SplitValue in Value.Split(Separator))
{
yield return SplitValue;
}
}
}
///
/// Applies these arguments to fields with the [CommandLine] attribute in the given object.
///
/// The object to configure
public void ApplyTo(object TargetObject)
{
// Build a mapping from name to field and attribute for this object
List MissingArguments = new List();
for(Type TargetType = TargetObject.GetType(); TargetType != typeof(object); TargetType = TargetType.BaseType)
{
foreach(FieldInfo FieldInfo in TargetType.GetFields(BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
// If any attribute is required, keep track of it so we can include an error for it
string RequiredPrefix = null;
// Keep track of whether a value has already been assigned to this field
string AssignedArgument = null;
// Loop through all the attributes for different command line options that can modify it
IEnumerable Attributes = FieldInfo.GetCustomAttributes();
foreach(CommandLineAttribute Attribute in Attributes)
{
// Get the appropriate prefix for this attribute
string Prefix = Attribute.Prefix;
if(Prefix == null)
{
if(FieldInfo.FieldType == typeof(bool))
{
Prefix = String.Format("-{0}", FieldInfo.Name);
}
else
{
Prefix = String.Format("-{0}=", FieldInfo.Name);
}
}
else
{
if(FieldInfo.FieldType != typeof(bool) && Attribute.Value == null && !Prefix.EndsWith("=") && !Prefix.EndsWith(":"))
{
Prefix = Prefix + "=";
}
}
// Get the value with the correct prefix
int FirstIndex;
if(ArgumentToFirstIndex.TryGetValue(Prefix, out FirstIndex))
{
for(int Index = FirstIndex; Index != -1; Index = NextArgumentIndex[Index])
{
// Get the argument text
string Argument = Arguments[Index];
// Get the text for this value
string ValueText;
if(Attribute.Value != null)
{
ValueText = Attribute.Value;
}
else if(FlagArguments.Get(Index))
{
ValueText = "true";
}
else
{
ValueText = Argument.Substring(Prefix.Length);
}
// Apply the value to the field
if(ApplyArgument(TargetObject, FieldInfo, Argument, ValueText, AssignedArgument))
{
AssignedArgument = Argument;
}
// Mark this argument as used
UsedArguments.Set(Index, true);
}
}
// If this attribute is marked as required, keep track of it so we can warn if the field is not assigned to
if(Attribute.Required && RequiredPrefix == null)
{
RequiredPrefix = Prefix;
}
}
// Make sure that this field has been assigned to
if(AssignedArgument == null && RequiredPrefix != null)
{
MissingArguments.Add(RequiredPrefix);
}
}
}
// If any arguments were missing, print an error about them
if(MissingArguments.Count > 0)
{
if(MissingArguments.Count == 1)
{
throw new CommandLineArgumentException(String.Format("Missing {0} argument", MissingArguments[0].Replace("=", "=...")));
}
else
{
throw new CommandLineArgumentException(String.Format("Missing {0} arguments", StringUtils.FormatList(MissingArguments.Select(x => x.Replace("=", "=...")))));
}
}
}
///
/// Splits a command line into individual arguments
///
/// The command line text
/// Array of arguments
public static string[] Split(string CommandLine)
{
StringBuilder Argument = new StringBuilder();
List Arguments = new List();
for(int Idx = 0; Idx < CommandLine.Length; Idx++)
{
if(!Char.IsWhiteSpace(CommandLine[Idx]))
{
Argument.Clear();
for(bool bInQuotes = false; Idx < CommandLine.Length; Idx++)
{
if(CommandLine[Idx] == '\"')
{
bInQuotes ^= true;
}
else if(!bInQuotes && Char.IsWhiteSpace(CommandLine[Idx]))
{
break;
}
else
{
Argument.Append(CommandLine[Idx]);
}
}
Arguments.Add(Argument.ToString());
}
}
return Arguments.ToArray();
}
///
/// Appends the given arguments to the current argument list
///
/// The arguments to add
/// New argument list
public CommandLineArguments Append(IEnumerable AppendArguments)
{
CommandLineArguments NewArguments = new CommandLineArguments(Enumerable.Concat(Arguments, AppendArguments).ToArray());
for(int Idx = 0; Idx < Arguments.Length; Idx++)
{
if(HasBeenUsed(Idx))
{
NewArguments.MarkAsUsed(Idx);
}
}
return NewArguments;
}
///
/// Retrieves all arguments with the given prefix, and returns the remaining a list of strings
///
/// Prefix for the arguments to remove
/// Receives a list of values with the given prefix
/// New argument list
public CommandLineArguments Remove(string Prefix, out List Values)
{
Values = new List();
// Split the arguments into the values array and an array of new arguments
int[] NewArgumentIndex = new int[Arguments.Length];
List NewArgumentList = new List(Arguments.Length);
for(int Idx = 0; Idx < Arguments.Length; Idx++)
{
string Argument = Arguments[Idx];
if(Argument.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase))
{
NewArgumentIndex[Idx] = -1;
Values.Add(Argument.Substring(Prefix.Length));
}
else
{
NewArgumentIndex[Idx] = NewArgumentList.Count;
NewArgumentList.Add(Argument);
}
}
// Create the new argument list, and mark the same arguments as used
CommandLineArguments NewArguments = new CommandLineArguments(NewArgumentList.ToArray());
for(int Idx = 0; Idx < Arguments.Length; Idx++)
{
if(HasBeenUsed(Idx) && NewArgumentIndex[Idx] != -1)
{
NewArguments.MarkAsUsed(NewArgumentIndex[Idx]);
}
}
return NewArguments;
}
///
/// Checks that there are no unused arguments (and warns if there are)
///
public void CheckAllArgumentsUsed()
{
// Find all the unused arguments
List RemainingArguments = new List();
for(int Idx = 0; Idx < Arguments.Length; Idx++)
{
if(!UsedArguments[Idx])
{
RemainingArguments.Add(Arguments[Idx]);
}
}
// Output a warning
if(RemainingArguments.Count > 0)
{
if(RemainingArguments.Count == 1)
{
Log.TraceWarning("Invalid argument: {0}", RemainingArguments[0]);
}
else
{
Log.TraceWarning("Invalid arguments:\n{0}", String.Join("\n", RemainingArguments));
}
}
}
///
/// Checks that a given string is a valid argument prefix
///
/// The prefix to check
private static void CheckValidPrefix(string Prefix)
{
if(Prefix.Length == 0)
{
throw new ArgumentException("Argument prefix cannot be empty.");
}
else if(Prefix[0] != '-')
{
throw new ArgumentException("Argument prefix must begin with a hyphen.");
}
else if(!ValueSeparators.Contains(Prefix[Prefix.Length - 1]))
{
throw new ArgumentException(String.Format("Argument prefix must end with '{0}'", String.Join("' or '", ValueSeparators)));
}
}
///
/// Parses and assigns a value to a field
///
/// The target object to assign values to
/// The field to assign the value to
/// The full argument text
/// Argument text
/// The previous text used to configure this field
/// True if the value was assigned to the field, false otherwise
private static bool ApplyArgument(object TargetObject, FieldInfo Field, string ArgumentText, string ValueText, string PreviousArgumentText)
{
// The value type for items of this field
Type ValueType = Field.FieldType;
// Check if the field type implements ICollection<>. If so, we can take multiple values.
Type CollectionType = null;
foreach (Type InterfaceType in Field.FieldType.GetInterfaces())
{
if (InterfaceType.IsGenericType && InterfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
ValueType = InterfaceType.GetGenericArguments()[0];
CollectionType = InterfaceType;
break;
}
}
// Try to parse the value
object Value;
if(!TryParseValue(ValueType, ValueText, out Value))
{
Log.TraceWarning("Unable to parse value for argument '{0}'.", ArgumentText);
return false;
}
// Try to assign values to the target field
if (CollectionType == null)
{
// Check if this field has already been assigned to. Output a warning if the previous value is in conflict with the new one.
if(PreviousArgumentText != null)
{
object PreviousValue = Field.GetValue(TargetObject);
if(!PreviousValue.Equals(Value))
{
Log.TraceWarning("Argument '{0}' conflicts with '{1}'; ignoring.", ArgumentText, PreviousArgumentText);
}
return false;
}
// Set the value on the target object
Field.SetValue(TargetObject, Value);
return true;
}
else
{
// Call the 'Add' method on the collection
CollectionType.InvokeMember("Add", BindingFlags.InvokeMethod, null, Field.GetValue(TargetObject), new object[] { Value });
return true;
}
}
///
/// Attempts to parse the given string to a value
///
/// Type of the field to convert to
/// The value text
/// On success, contains the parsed object
/// True if the text could be parsed, false otherwise
private static bool TryParseValue(Type FieldType, string Text, out object Value)
{
if(FieldType.IsEnum)
{
// Special handling for enums; parse the value ignoring case.
try
{
Value = Enum.Parse(FieldType, Text, true);
return true;
}
catch(ArgumentException)
{
Value = null;
return false;
}
}
else if(FieldType == typeof(FileReference))
{
// Construct a file reference from the string
try
{
Value = new FileReference(Text);
return true;
}
catch
{
Value = null;
return false;
}
}
else if(FieldType == typeof(DirectoryReference))
{
// Construct a file reference from the string
try
{
Value = new DirectoryReference(Text);
return true;
}
catch
{
Value = null;
return false;
}
}
else
{
// Otherwise let the framework convert between types
try
{
Value = Convert.ChangeType(Text, FieldType);
return true;
}
catch(InvalidCastException)
{
Value = null;
return false;
}
}
}
///
/// Obtains an enumerator for the argument list
///
/// IEnumerator interface
IEnumerator IEnumerable.GetEnumerator()
{
return Arguments.GetEnumerator();
}
///
/// Obtains an enumerator for the argument list
///
/// Generic IEnumerator interface
public IEnumerator GetEnumerator()
{
return ((IEnumerable)Arguments).GetEnumerator();
}
///
/// Gets the raw argument array
///
/// Array of arguments
public string[] GetRawArray()
{
return Arguments;
}
///
/// Takes a command line argument and adds quotes if necessary
///
/// The command line argument
/// The command line argument with quotes inserted to escape it if necessary
public static void Append(StringBuilder CommandLine, string Argument)
{
if(CommandLine.Length > 0)
{
CommandLine.Append(' ');
}
int SpaceIdx = Argument.IndexOf(' ');
if(SpaceIdx == -1)
{
CommandLine.Append(Argument);
}
else
{
int EqualsIdx = Argument.IndexOf('=');
if(EqualsIdx == -1)
{
CommandLine.AppendFormat("\"{0}\"", Argument);
}
else
{
CommandLine.AppendFormat("{0}\"{1}\"", Argument.Substring(0, EqualsIdx + 1), Argument.Substring(EqualsIdx + 1));
}
}
}
///
/// Converts this string to
///
///
public override string ToString()
{
StringBuilder Result = new StringBuilder();
foreach(string Argument in Arguments)
{
Append(Result, Argument);
}
return Result.ToString();
}
}
}