From f2280629b9f9c1df5a223165fafde6755517697c Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Thu, 23 Apr 2009 20:23:23 +0000 Subject: [PATCH] The command line class can now also be used to build a command line (for example, to pass it to another program); removed dead code from the command line class git-svn-id: file:///srv/devel/repo-conversion/nusu@132 d2e56fa2-650e-0410-a79f-9358c0239efd --- Source/Licensing/LicenseKey.Test.cs | 1 - Source/Parsing/CommandLine.Argument.cs | 5 + Source/Parsing/CommandLine.Formatter.cs | 33 +++++- Source/Parsing/CommandLine.Parser.cs | 39 ------- Source/Parsing/CommandLine.Test.cs | 46 +++++++- Source/Parsing/CommandLine.cs | 147 +++++++++++++++--------- 6 files changed, 175 insertions(+), 96 deletions(-) diff --git a/Source/Licensing/LicenseKey.Test.cs b/Source/Licensing/LicenseKey.Test.cs index a8bbc47..d4d6316 100644 --- a/Source/Licensing/LicenseKey.Test.cs +++ b/Source/Licensing/LicenseKey.Test.cs @@ -37,7 +37,6 @@ namespace Nuclex.Support.Licensing { new LicenseKey(); } - /// Validates the correct translation of keys to GUIDs and back [Test] public void TestGuidKeyConversion() { diff --git a/Source/Parsing/CommandLine.Argument.cs b/Source/Parsing/CommandLine.Argument.cs index 1b7a914..2391393 100644 --- a/Source/Parsing/CommandLine.Argument.cs +++ b/Source/Parsing/CommandLine.Argument.cs @@ -140,6 +140,11 @@ namespace Nuclex.Support.Parsing { } } + /// The raw length of the command line argument + internal int RawLength { + get { return this.raw.Count; } + } + /// /// Contains the entire option as it was specified on the command line /// diff --git a/Source/Parsing/CommandLine.Formatter.cs b/Source/Parsing/CommandLine.Formatter.cs index a654353..079dc96 100644 --- a/Source/Parsing/CommandLine.Formatter.cs +++ b/Source/Parsing/CommandLine.Formatter.cs @@ -20,12 +20,43 @@ License along with this library using System; using System.Collections.Generic; +using System.Text; namespace Nuclex.Support.Parsing { partial class CommandLine { - internal static class Formatter { } + /// Formats a command line instance into a string + internal static class Formatter { + + /// + /// Formats all arguments in the provided command line instance into a string + /// + /// Command line instance that will be formatted + /// All arguments in the command line instance as a string + public static string FormatCommandLine(CommandLine commandLine) { + int totalLength = 0; + for(int index = 0; index < commandLine.arguments.Count; ++index) { + if(index != 0) { + ++totalLength; // For spacing between arguments + } + + totalLength += commandLine.arguments[index].RawLength; + } + + StringBuilder builder = new StringBuilder(totalLength); + for(int index = 0; index < commandLine.arguments.Count; ++index) { + if(index != 0) { + builder.Append(' '); + } + + builder.Append(commandLine.arguments[index].Raw); + } + + return builder.ToString(); + } + + } } diff --git a/Source/Parsing/CommandLine.Parser.cs b/Source/Parsing/CommandLine.Parser.cs index cfd6438..7de3763 100644 --- a/Source/Parsing/CommandLine.Parser.cs +++ b/Source/Parsing/CommandLine.Parser.cs @@ -49,20 +49,6 @@ namespace Nuclex.Support.Parsing { return theParser.arguments; } -#if ENABLE_TOKENIZED_COMMAND_LINE_PARSING // don't enable, it's broken! - /// Parses a string containing command line arguments - /// Command line tokens that will be parsed - /// Whether the / character initiates an argument - /// The parsed command line arguments from the string - public static List Parse( - string[] commandLineArguments, bool windowsMode - ) { - Parser theParser = new Parser(windowsMode); - theParser.parseSplitCommandLine(commandLineArguments); - return theParser.arguments; - } -#endif // ENABLE_TOKENIZED_COMMAND_LINE_PARSING - /// /// Parses the provided string and adds the parameters found to /// the command line representation @@ -94,31 +80,6 @@ namespace Nuclex.Support.Parsing { } } -#if ENABLE_TOKENIZED_COMMAND_LINE_PARSING // don't enable, it's broken! - /// - /// Parses the command line from a series pre-split argument tokens - /// - /// Split argument tokens that will be parsed - private void parseSplitCommandLine(string[] commandLineParts) { - if(commandLineParts == null) { - return; - } - - // Walk through the command line character by character and gather - // the parameters and values to build the command line representation from - for(int index = 0; index < commandLineParts.Length; ++index) { - - if(commandLineParts[index] != null) { - int characterIndex = 0; - parseChunk(commandLineParts[index], ref characterIndex); - - Debug.Assert(characterIndex == commandLineParts[index].Length); - } - - } - } -#endif // ENABLE_TOKENIZED_COMMAND_LINE_PARSING - /// /// Parses a chunk of characters and adds it as an option or a loose value to /// the command line representation we're building diff --git a/Source/Parsing/CommandLine.Test.cs b/Source/Parsing/CommandLine.Test.cs index c53a38e..3e4e440 100644 --- a/Source/Parsing/CommandLine.Test.cs +++ b/Source/Parsing/CommandLine.Test.cs @@ -579,8 +579,52 @@ namespace Nuclex.Support.Parsing { Assert.IsFalse(test.HasArgument("fourth")); } + /// + /// Tests whether a command line can be built with the command line class + /// + [Test] + public void TestCommandLineFormatting() { + CommandLine commandLine = new CommandLine(); + + commandLine.AddValue("single"); + commandLine.AddValue("with space"); + commandLine.AddOption("option"); + commandLine.AddOption("@@", "extravagant-option"); + commandLine.AddAssignment("name", "value"); + commandLine.AddAssignment("name", "value with spaces"); + commandLine.AddAssignment("@@", "name", "value"); + commandLine.AddAssignment("@@", "name", "value with spaces"); + + Assert.AreEqual(8, commandLine.Arguments.Count); + Assert.AreEqual("single", commandLine.Arguments[0].Value); + Assert.AreEqual("with space", commandLine.Arguments[1].Value); + Assert.AreEqual("option", commandLine.Arguments[2].Name); + Assert.AreEqual("@@", commandLine.Arguments[3].Initiator); + Assert.AreEqual("extravagant-option", commandLine.Arguments[3].Name); + Assert.AreEqual("name", commandLine.Arguments[4].Name); + Assert.AreEqual("value", commandLine.Arguments[4].Value); + Assert.AreEqual("name", commandLine.Arguments[5].Name); + Assert.AreEqual("value with spaces", commandLine.Arguments[5].Value); + Assert.AreEqual("@@", commandLine.Arguments[6].Initiator); + Assert.AreEqual("name", commandLine.Arguments[6].Name); + Assert.AreEqual("value", commandLine.Arguments[6].Value); + Assert.AreEqual("name", commandLine.Arguments[7].Name); + Assert.AreEqual("@@", commandLine.Arguments[7].Initiator); + Assert.AreEqual("value with spaces", commandLine.Arguments[7].Value); + + string commandLineString = commandLine.ToString(); + Assert.AreEqual( + "single \"with space\" " + + "-option @@extravagant-option " + + "-name=value -name=\"value with spaces\" " + + "@@name=value @@name=\"value with spaces\"", + commandLineString + ); + + } + } } // namespace Nuclex.Support.Parsing -#endif // UNITTEST \ No newline at end of file +#endif // UNITTEST diff --git a/Source/Parsing/CommandLine.cs b/Source/Parsing/CommandLine.cs index 51f7d9e..0e9d45a 100644 --- a/Source/Parsing/CommandLine.cs +++ b/Source/Parsing/CommandLine.cs @@ -91,26 +91,6 @@ namespace Nuclex.Support.Parsing { this.arguments = argumentList; } -#if ENABLE_TOKENIZED_COMMAND_LINE_PARSING // don't enable, it's broken! - /// Parses the command line arguments from the provided string - /// Command line tokens that will be parsed - /// The parsed command line - public static CommandLine Parse(string[] commandLineArguments) { - bool windowsMode = (Path.DirectorySeparatorChar != '/'); - return Parse(commandLineArguments, windowsMode); - } - - /// Parses the command line arguments from the provided string - /// Command line tokens that will be parsed - /// Whether the / character initiates an argument - /// The parsed command line - public static CommandLine Parse(string[] commandLineArguments, bool windowsMode) { - return new CommandLine( - Parser.Parse(commandLineArguments, windowsMode) - ); - } -#endif // ENABLE_TOKENIZED_COMMAND_LINE_PARSING - /// Parses the command line arguments from the provided string /// String containing the command line arguments /// The parsed command line @@ -146,41 +126,101 @@ namespace Nuclex.Support.Parsing { return (indexOfArgument(name) != -1); } -#if false - /// Retrieves the value of the specified argument - /// Name of the argument whose value will be retrieved - /// The value of the specified argument - public string GetValue(string name) { - int index = indexOfArgument(name); - if(index == -1) { - return null; + /// Adds a value to the command line + /// Value that will be added + public void AddValue(string value) { + bool valueContainsSpaces = (value.IndexOfAny(new char[] { ' ', '\t' }) != -1); + + if(valueContainsSpaces) { + StringBuilder builder = new StringBuilder(value.Length + 2); + builder.Append('"'); + builder.Append(value); + builder.Append('"'); + + this.arguments.Add( + Argument.ValueOnly( + new StringSegment(builder.ToString(), 0, value.Length + 2), + 1, + value.Length + ) + ); + } else { + this.arguments.Add( + Argument.ValueOnly(new StringSegment(value), 0, value.Length) + ); } - - // Does this argument have a value? - Argument argument = this.arguments[index]; - if(argument.Value != null) { - return argument.Value; - } else { // No, it might be a spaced argument - - // See if anything more follows this argument - ++index; - if(index < this.arguments.Count) { - - // If something follows the argument, and it is not an option of its own, - // use its value as the value for the preceding argument - argument = this.arguments[index]; - if(argument.Name == null) { - return argument.Value; - } - - } - - } - - // No argument found - return null; } -#endif + + /// Adds an option to the command line + /// Name of the option that will be added + public void AddOption(string name) { + AddOption("-", name); + } + + /// Adds an option to the command line + /// Initiator that will be used to start the option + /// Name of the option that will be added + public void AddOption(string initiator, string name) { + StringBuilder builder = new StringBuilder( + initiator.Length + name.Length + ); + builder.Append(initiator); + builder.Append(name); + + this.arguments.Add( + Argument.OptionOnly( + new StringSegment(builder.ToString()), + initiator.Length, + name.Length + ) + ); + } + + /// Adds an option with an assignment to the command line + /// Name of the option that will be added + /// Value that will be assigned to the option + public void AddAssignment(string name, string value) { + AddAssignment("-", name, value); + } + + /// Adds an option with an assignment to the command line + /// Initiator that will be used to start the option + /// Name of the option that will be added + /// Value that will be assigned to the option + public void AddAssignment(string initiator, string name, string value) { + bool valueContainsSpaces = (value.IndexOfAny(new char[] { ' ', '\t' }) != -1); + StringBuilder builder = new StringBuilder( + initiator.Length + name.Length + 1 + value.Length + + (valueContainsSpaces ? 2 : 0) + ); + builder.Append(initiator); + builder.Append(name); + builder.Append('='); + if(valueContainsSpaces) { + builder.Append('"'); + builder.Append(value); + builder.Append('"'); + } else { + builder.Append(value); + } + + this.arguments.Add( + new Argument( + new StringSegment(builder.ToString()), + initiator.Length, + name.Length, + initiator.Length + name.Length + 1 + + (valueContainsSpaces ? 1 : 0), + value.Length + ) + ); + } + + /// Returns a string that contains the entire command line + /// The entire command line as a single string + public override string ToString() { + return Formatter.FormatCommandLine(this); + } /// Retrieves the index of the argument with the specified name /// Name of the argument whose index will be returned @@ -197,7 +237,6 @@ namespace Nuclex.Support.Parsing { return -1; } - /// Options that were specified on the command line public IList Arguments { get { return this.arguments; }