Removed the old command line parser since its replacement is fully functional by now; the CommandLine class now provides a HasArgument() method for convenience; added experimental parsing for pre-split command lines (as provided by .NET's Main() method) and disabled it because it turned out to be a bad idea
git-svn-id: file:///srv/devel/repo-conversion/nusu@130 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
47b0039137
commit
a2331b95c1
|
@ -157,10 +157,6 @@
|
||||||
<Compile Include="Source\Parsing\CommandLine.Test.cs">
|
<Compile Include="Source\Parsing\CommandLine.Test.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Parsing\BrokenCommandLineParser.cs" />
|
|
||||||
<Compile Include="Source\Parsing\BrokenCommandLineParser.Test.cs">
|
|
||||||
<DependentUpon>BrokenCommandLineParser.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Parsing\CommandLine.cs" />
|
<Compile Include="Source\Parsing\CommandLine.cs" />
|
||||||
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
|
|
|
@ -139,10 +139,6 @@
|
||||||
<Compile Include="Source\Parsing\CommandLine.Test.cs">
|
<Compile Include="Source\Parsing\CommandLine.Test.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Parsing\BrokenCommandLineParser.cs" />
|
|
||||||
<Compile Include="Source\Parsing\BrokenCommandLineParser.Test.cs">
|
|
||||||
<DependentUpon>BrokenCommandLineParser.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Parsing\CommandLine.cs" />
|
<Compile Include="Source\Parsing\CommandLine.cs" />
|
||||||
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
#region CPL License
|
|
||||||
/*
|
|
||||||
Nuclex Framework
|
|
||||||
Copyright (C) 2002-2009 Nuclex Development Labs
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the IBM Common Public License as
|
|
||||||
published by the IBM Corporation; either version 1.0 of the
|
|
||||||
License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
IBM Common Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the IBM Common Public
|
|
||||||
License along with this library
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
#if UNITTEST
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
#if ENABLE_BROKEN_COMMAND_LINE_PARSER // Too bugged. 100% test coverage not possible.
|
|
||||||
|
|
||||||
namespace Nuclex.Support.Parsing {
|
|
||||||
|
|
||||||
/// <summary>Ensures that the command line parser is working properly</summary>
|
|
||||||
[TestFixture]
|
|
||||||
public class CommandLineParserTest {
|
|
||||||
|
|
||||||
/// <summary>Validates that normal arguments can be parsed</summary>
|
|
||||||
[Test]
|
|
||||||
public void TestArrayConstructorWithPlainArguments() {
|
|
||||||
Assert.IsTrue(
|
|
||||||
new CommandLineParser(new string[] { "-hello" }).HasArgument("hello"),
|
|
||||||
"Argument with minus sign is recognized"
|
|
||||||
);
|
|
||||||
Assert.IsTrue(
|
|
||||||
new CommandLineParser(new string[] { "--hello" }).HasArgument("hello"),
|
|
||||||
"Argument with double minus sign is recognized"
|
|
||||||
);
|
|
||||||
Assert.IsTrue(
|
|
||||||
new CommandLineParser(new string[] { "/hello" }).HasArgument("hello"),
|
|
||||||
"Argument with slash is recognized"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Validates that argument assignments are working</summary>
|
|
||||||
[Test]
|
|
||||||
public void TestArrayConstructorWithAssignments() {
|
|
||||||
Assert.AreEqual(
|
|
||||||
"world",
|
|
||||||
new CommandLineParser(new string[] { "-hello:world" })["hello"],
|
|
||||||
"Argument can be assigned with a double colon"
|
|
||||||
);
|
|
||||||
Assert.AreEqual(
|
|
||||||
"world",
|
|
||||||
new CommandLineParser(new string[] { "-hello=world" })["hello"],
|
|
||||||
"Argument can be assigned with a equality sign"
|
|
||||||
);
|
|
||||||
Assert.AreEqual(
|
|
||||||
"world",
|
|
||||||
new CommandLineParser(new string[] { "-hello", "world" })["hello"],
|
|
||||||
"Argument can be assigned with a space"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validates that loosely specified values are recognized by the parser
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestArrayConstructorWithLooseValues() {
|
|
||||||
Assert.IsTrue(
|
|
||||||
new CommandLineParser(new string[] { "hello" }).Values.Contains("hello"),
|
|
||||||
"Plain loose value is recognized"
|
|
||||||
);
|
|
||||||
Assert.IsTrue(
|
|
||||||
new CommandLineParser(new string[] { "-hello:world", "foo" }).Values.Contains("foo"),
|
|
||||||
"Loose value following an assignment is recognized"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tests whether the parser can parse the processes current command line if
|
|
||||||
/// the default constructor is used
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestDefaultConstructor() {
|
|
||||||
new CommandLineParser();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tests whether the string constructor works for simple arguments being
|
|
||||||
/// specified on the command line
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestStringConstructorWithSimpleArguments() {
|
|
||||||
CommandLineParser parser = new CommandLineParser("argument1 argument2");
|
|
||||||
Assert.AreEqual("argument1", parser.Values[0]);
|
|
||||||
Assert.AreEqual("argument2", parser.Values[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This test fails!!
|
|
||||||
#if FAILED_TEST
|
|
||||||
/// <summary>
|
|
||||||
/// Bullshit
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestStringConstructorWithQuotedArguments() {
|
|
||||||
CommandLineParser parser = new CommandLineParser("\"this is a single argument\"");
|
|
||||||
Assert.AreEqual("this is a single argument", parser.Values[0]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tests whether the string constructor recognizes an unfinished argument
|
|
||||||
/// (that is, an argument that gets 'nothing' assigned)
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestStringConstructorWithUnfinishedAssignment() {
|
|
||||||
CommandLineParser parser = new CommandLineParser("--hello= --world=");
|
|
||||||
Assert.AreEqual(0, parser.Values.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tests whether the string constructor recognizes an argument with a space before
|
|
||||||
/// its assigned value
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestStringConstructorWithSpacedAssignment() {
|
|
||||||
CommandLineParser parser = new CommandLineParser("--hello= world");
|
|
||||||
Assert.AreEqual(1, parser.Values.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Parsing
|
|
||||||
|
|
||||||
#endif // ENABLE_BROKEN_COMMAND_LINE_PARSER
|
|
||||||
|
|
||||||
#endif // UNITTEST
|
|
|
@ -1,185 +0,0 @@
|
||||||
#region CPL License
|
|
||||||
/*
|
|
||||||
Nuclex Framework
|
|
||||||
Copyright (C) 2002-2009 Nuclex Development Labs
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the IBM Common Public License as
|
|
||||||
published by the IBM Corporation; either version 1.0 of the
|
|
||||||
License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
IBM Common Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the IBM Common Public
|
|
||||||
License along with this library
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
#if ENABLE_BROKEN_COMMAND_LINE_PARSER // Too bugged. 100% test coverage not possible.
|
|
||||||
|
|
||||||
namespace Nuclex.Support.Parsing {
|
|
||||||
|
|
||||||
/// <summary>Parses an application's command line</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// Based on an article Richard Lopes published on "The Code Project" at
|
|
||||||
/// http://www.codeproject.com/csharp/command_line.asp
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Valid forms for command line arguments: {-|/|--}param[{ |=|:}[{"|'}]value[{"|'}]]
|
|
||||||
/// </para>
|
|
||||||
/// <example>
|
|
||||||
/// <code>
|
|
||||||
/// -param1 value1
|
|
||||||
/// --param2
|
|
||||||
/// /param3:"Test-:-work"
|
|
||||||
/// /param4=happy
|
|
||||||
/// -param5 '--=nice=--'
|
|
||||||
/// </code>
|
|
||||||
/// </example>
|
|
||||||
/// </remarks>
|
|
||||||
public class CommandLineParser {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new command line parser using the running program's command line
|
|
||||||
/// </summary>
|
|
||||||
public CommandLineParser() : this(System.Environment.CommandLine) { }
|
|
||||||
|
|
||||||
/// <summary>Initializes a new command line parser</summary>
|
|
||||||
/// <param name="arguments">All supplied command line arguments as a single string</param>
|
|
||||||
public CommandLineParser(string arguments)
|
|
||||||
: this(arguments.Split(new char[] { ' ', '\t' })) { }
|
|
||||||
|
|
||||||
/// <summary>Initializes a new command line parser</summary>
|
|
||||||
/// <param name="arguments">Arguments that have been passed in the command line</param>
|
|
||||||
public CommandLineParser(string[] arguments) {
|
|
||||||
this.arguments = new StringDictionary();
|
|
||||||
this.values = new StringCollection();
|
|
||||||
|
|
||||||
string activeParameter = null;
|
|
||||||
|
|
||||||
foreach(string argument in arguments) {
|
|
||||||
|
|
||||||
// Look for arguments ('-', '/', '--') with their assignments ('=', ':')
|
|
||||||
string[] parts = splitter.Split(argument, 3);
|
|
||||||
switch(parts.Length) {
|
|
||||||
|
|
||||||
// Value found without an argument being specified (eg. file name)
|
|
||||||
case 1: {
|
|
||||||
|
|
||||||
if(activeParameter != null) {
|
|
||||||
if(!this.arguments.ContainsKey(activeParameter)) {
|
|
||||||
parts[0] = remover.Replace(parts[0], "$1");
|
|
||||||
this.arguments.Add(activeParameter, parts[0]);
|
|
||||||
}
|
|
||||||
activeParameter = null;
|
|
||||||
} else {
|
|
||||||
this.values.Add(parts[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error: No argument is waiting for a value. Skip this argument.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found an argument with no value assignment
|
|
||||||
case 2: {
|
|
||||||
|
|
||||||
// In case the previous argument is still waiting for a value we need to finish
|
|
||||||
// it up before switching to the argument we just found.
|
|
||||||
if(activeParameter != null)
|
|
||||||
if(!this.arguments.ContainsKey(activeParameter))
|
|
||||||
this.arguments.Add(activeParameter, null);
|
|
||||||
|
|
||||||
// Remember argument to allow for a later value assignment
|
|
||||||
activeParameter = parts[1];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found an argument with a proper assignment declaration
|
|
||||||
case 3: {
|
|
||||||
|
|
||||||
// In case the previous argument is still waiting for a value we need to finish
|
|
||||||
// it up before switching to the argument we just found.
|
|
||||||
if(activeParameter != null)
|
|
||||||
if(!this.arguments.ContainsKey(activeParameter))
|
|
||||||
this.arguments.Add(activeParameter, null);
|
|
||||||
|
|
||||||
activeParameter = parts[1];
|
|
||||||
|
|
||||||
// Remove any quotes that might be enclosing this argument (",')
|
|
||||||
if(!this.arguments.ContainsKey(activeParameter)) {
|
|
||||||
parts[2] = remover.Replace(parts[2], "$1");
|
|
||||||
this.arguments.Add(activeParameter, parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
activeParameter = null;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the previous argument is still waiting for a value we need to finish
|
|
||||||
// it up before leaving the parsing method.
|
|
||||||
if(activeParameter != null) {
|
|
||||||
if(!this.arguments.ContainsKey(activeParameter)) {
|
|
||||||
this.arguments.Add(activeParameter, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Returns the value of an argument by the argument's name</summary>
|
|
||||||
/// <param name="argumentName">Name of the argument whose value will be returned</param>
|
|
||||||
/// <returns>The value of the argument with the specified name</returns>
|
|
||||||
public string this[string argumentName] {
|
|
||||||
get { return this.arguments[argumentName]; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks whether the specified argument was specified on the command line
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="argumentName">Name of the argument to check</param>
|
|
||||||
/// <returns>True if the specified command was given on the command line</returns>
|
|
||||||
public bool HasArgument(string argumentName) {
|
|
||||||
return this.arguments.ContainsKey(argumentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any values loosely specified on the command line without being assigned
|
|
||||||
/// to an argument.
|
|
||||||
/// </summary>
|
|
||||||
public StringCollection Values {
|
|
||||||
get { return this.values; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Regular Expression used to split the arguments and their assigned values
|
|
||||||
/// </summary>
|
|
||||||
private static Regex splitter =
|
|
||||||
new Regex(@"^-{1,2}|^/|=|:", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Regular Expression used to remove quotations around an argument's value
|
|
||||||
/// </summary>
|
|
||||||
private static Regex remover =
|
|
||||||
new Regex(@"^['""]?(.*?)['""]?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
||||||
|
|
||||||
/// <summary>Stores the parsed arguments</summary>
|
|
||||||
private StringDictionary arguments;
|
|
||||||
/// <summary>
|
|
||||||
/// Stores any values passed on the command line without assigning an argument
|
|
||||||
/// </summary>
|
|
||||||
private StringCollection values;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Parsing
|
|
||||||
|
|
||||||
#endif // ENABLE_BROKEN_COMMAND_LINE_PARSER
|
|
|
@ -34,20 +34,35 @@ namespace Nuclex.Support.Parsing {
|
||||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||||
private Parser(bool windowsMode) {
|
private Parser(bool windowsMode) {
|
||||||
this.windowsMode = windowsMode;
|
this.windowsMode = windowsMode;
|
||||||
this.commandLine = new CommandLine();
|
this.arguments = new List<CommandLine.Argument>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Parses a string containing command line arguments</summary>
|
/// <summary>Parses a string containing command line arguments</summary>
|
||||||
/// <param name="commandLineString">String that will be parsed</param>
|
/// <param name="commandLineString">String that will be parsed</param>
|
||||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||||
/// <returns>The parsed command line arguments from the string</returns>
|
/// <returns>The parsed command line arguments from the string</returns>
|
||||||
public static CommandLine Parse(string commandLineString, bool windowsMode) {
|
public static List<CommandLine.Argument> Parse(
|
||||||
Console.WriteLine("Parsing '" + commandLineString + "'");
|
string commandLineString, bool windowsMode
|
||||||
|
) {
|
||||||
Parser theParser = new Parser(windowsMode);
|
Parser theParser = new Parser(windowsMode);
|
||||||
theParser.parseFullCommandLine(commandLineString);
|
theParser.parseFullCommandLine(commandLineString);
|
||||||
return theParser.commandLine;
|
return theParser.arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_TOKENIZED_COMMAND_LINE_PARSING // don't enable, it's broken!
|
||||||
|
/// <summary>Parses a string containing command line arguments</summary>
|
||||||
|
/// <param name="commandLineArguments">Command line tokens that will be parsed</param>
|
||||||
|
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||||
|
/// <returns>The parsed command line arguments from the string</returns>
|
||||||
|
public static List<CommandLine.Argument> Parse(
|
||||||
|
string[] commandLineArguments, bool windowsMode
|
||||||
|
) {
|
||||||
|
Parser theParser = new Parser(windowsMode);
|
||||||
|
theParser.parseSplitCommandLine(commandLineArguments);
|
||||||
|
return theParser.arguments;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TOKENIZED_COMMAND_LINE_PARSING
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses the provided string and adds the parameters found to
|
/// Parses the provided string and adds the parameters found to
|
||||||
/// the command line representation
|
/// the command line representation
|
||||||
|
@ -79,6 +94,31 @@ namespace Nuclex.Support.Parsing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_TOKENIZED_COMMAND_LINE_PARSING // don't enable, it's broken!
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the command line from a series pre-split argument tokens
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commandLineParts">Split argument tokens that will be parsed</param>
|
||||||
|
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
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses a chunk of characters and adds it as an option or a loose value to
|
/// Parses a chunk of characters and adds it as an option or a loose value to
|
||||||
/// the command line representation we're building
|
/// the command line representation we're building
|
||||||
|
@ -100,7 +140,7 @@ namespace Nuclex.Support.Parsing {
|
||||||
|
|
||||||
// Does the string end here? Stop parsing.
|
// Does the string end here? Stop parsing.
|
||||||
if(index >= commandLineString.Length) {
|
if(index >= commandLineString.Length) {
|
||||||
this.commandLine.addValue(new StringSegment(commandLineString, startIndex, 1));
|
addValue(new StringSegment(commandLineString, startIndex, 1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +198,7 @@ namespace Nuclex.Support.Parsing {
|
||||||
|
|
||||||
// If the string ends here this can only be considered as a loose value
|
// If the string ends here this can only be considered as a loose value
|
||||||
if(index == commandLineString.Length) {
|
if(index == commandLineString.Length) {
|
||||||
this.commandLine.addValue(
|
addValue(
|
||||||
new StringSegment(
|
new StringSegment(
|
||||||
commandLineString,
|
commandLineString,
|
||||||
initiatorStartIndex,
|
initiatorStartIndex,
|
||||||
|
@ -187,7 +227,7 @@ namespace Nuclex.Support.Parsing {
|
||||||
index = commandLineString.Length;
|
index = commandLineString.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandLine.addValue(
|
addValue(
|
||||||
new StringSegment(
|
new StringSegment(
|
||||||
commandLineString, initiatorStartIndex, index - initiatorStartIndex
|
commandLineString, initiatorStartIndex, index - initiatorStartIndex
|
||||||
)
|
)
|
||||||
|
@ -245,7 +285,7 @@ namespace Nuclex.Support.Parsing {
|
||||||
}
|
}
|
||||||
|
|
||||||
int argumentLength = index - initiatorStartIndex;
|
int argumentLength = index - initiatorStartIndex;
|
||||||
this.commandLine.addArgument(
|
this.arguments.Add(
|
||||||
new Argument(
|
new Argument(
|
||||||
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
||||||
nameStartIndex, nameEndIndex - nameStartIndex,
|
nameStartIndex, nameEndIndex - nameStartIndex,
|
||||||
|
@ -308,7 +348,7 @@ namespace Nuclex.Support.Parsing {
|
||||||
}
|
}
|
||||||
|
|
||||||
int argumentLength = index - initiatorStartIndex;
|
int argumentLength = index - initiatorStartIndex;
|
||||||
this.commandLine.addArgument(
|
this.arguments.Add(
|
||||||
new Argument(
|
new Argument(
|
||||||
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
||||||
nameStartIndex, nameEndIndex - nameStartIndex,
|
nameStartIndex, nameEndIndex - nameStartIndex,
|
||||||
|
@ -329,14 +369,14 @@ namespace Nuclex.Support.Parsing {
|
||||||
index = commandLineString.IndexOf(quoteCharacter, valueIndex);
|
index = commandLineString.IndexOf(quoteCharacter, valueIndex);
|
||||||
if(index == -1) {
|
if(index == -1) {
|
||||||
index = commandLineString.Length; // value ends at string end
|
index = commandLineString.Length; // value ends at string end
|
||||||
commandLine.addArgument(
|
this.arguments.Add(
|
||||||
Argument.ValueOnly(
|
Argument.ValueOnly(
|
||||||
new StringSegment(commandLineString, startIndex, index - startIndex),
|
new StringSegment(commandLineString, startIndex, index - startIndex),
|
||||||
valueIndex, index - valueIndex
|
valueIndex, index - valueIndex
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else { // A closing quote was found
|
} else { // A closing quote was found
|
||||||
commandLine.addArgument(
|
this.arguments.Add(
|
||||||
Argument.ValueOnly(
|
Argument.ValueOnly(
|
||||||
new StringSegment(commandLineString, startIndex, index - startIndex + 1),
|
new StringSegment(commandLineString, startIndex, index - startIndex + 1),
|
||||||
valueIndex, index - valueIndex
|
valueIndex, index - valueIndex
|
||||||
|
@ -357,8 +397,14 @@ namespace Nuclex.Support.Parsing {
|
||||||
index = commandLineString.Length;
|
index = commandLineString.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandLine.addValue(
|
addValue(new StringSegment(commandLineString, startIndex, index - startIndex));
|
||||||
new StringSegment(commandLineString, startIndex, index - startIndex)
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds a loose value to the command line</summary>
|
||||||
|
/// <param name="value">Value taht will be added</param>
|
||||||
|
private void addValue(StringSegment value) {
|
||||||
|
this.arguments.Add(
|
||||||
|
Argument.ValueOnly(value, value.Offset, value.Count)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,8 +429,8 @@ namespace Nuclex.Support.Parsing {
|
||||||
/// <summary>Characters the parser considers to be whitespace</summary>
|
/// <summary>Characters the parser considers to be whitespace</summary>
|
||||||
private static readonly char[] WhitespaceCharacters = new char[] { ' ', '\t' };
|
private static readonly char[] WhitespaceCharacters = new char[] { ' ', '\t' };
|
||||||
|
|
||||||
/// <summary>Command line currently being built by the parser</summary>
|
/// <summary>Argument list being filled by the parser</summary>
|
||||||
private CommandLine commandLine;
|
private List<CommandLine.Argument> arguments;
|
||||||
/// <summary>Whether the '/' character initiates an argument</summary>
|
/// <summary>Whether the '/' character initiates an argument</summary>
|
||||||
private bool windowsMode;
|
private bool windowsMode;
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,14 @@ namespace Nuclex.Support.Parsing {
|
||||||
|
|
||||||
#endregion // class ArgumentTest
|
#endregion // class ArgumentTest
|
||||||
|
|
||||||
|
/// <summary>Verifies that the default constructor is working</summary>
|
||||||
|
[Test]
|
||||||
|
public void TestDefaultConstructor() {
|
||||||
|
CommandLine commandLine = new CommandLine();
|
||||||
|
|
||||||
|
Assert.AreEqual(0, commandLine.Arguments.Count);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates that the parser can handle an argument initiator with an
|
/// Validates that the parser can handle an argument initiator with an
|
||||||
/// assignment that is missing a name
|
/// assignment that is missing a name
|
||||||
|
@ -302,7 +310,7 @@ namespace Nuclex.Support.Parsing {
|
||||||
/// <summary>Validates that null can be parsed</summary>
|
/// <summary>Validates that null can be parsed</summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestParseNull() {
|
public void TestParseNull() {
|
||||||
CommandLine commandLine = CommandLine.Parse(null);
|
CommandLine commandLine = CommandLine.Parse((string)null);
|
||||||
|
|
||||||
Assert.AreEqual(0, commandLine.Arguments.Count);
|
Assert.AreEqual(0, commandLine.Arguments.Count);
|
||||||
}
|
}
|
||||||
|
@ -558,6 +566,19 @@ namespace Nuclex.Support.Parsing {
|
||||||
Assert.AreEqual("//world", commandLine.Arguments[1].Value);
|
Assert.AreEqual("//world", commandLine.Arguments[1].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the existence of named arguments can be checked
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestHasArgument() {
|
||||||
|
CommandLine test = CommandLine.Parse("/first:x /second:y /second:z third");
|
||||||
|
|
||||||
|
Assert.IsTrue(test.HasArgument("first"));
|
||||||
|
Assert.IsTrue(test.HasArgument("second"));
|
||||||
|
Assert.IsFalse(test.HasArgument("third"));
|
||||||
|
Assert.IsFalse(test.HasArgument("fourth"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Parsing
|
} // namespace Nuclex.Support.Parsing
|
||||||
|
|
|
@ -21,6 +21,7 @@ License along with this library
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
using Nuclex.Support.Collections;
|
using Nuclex.Support.Collections;
|
||||||
|
|
||||||
|
@ -82,55 +83,120 @@ namespace Nuclex.Support.Parsing {
|
||||||
public partial class CommandLine {
|
public partial class CommandLine {
|
||||||
|
|
||||||
/// <summary>Initializes a new command line</summary>
|
/// <summary>Initializes a new command line</summary>
|
||||||
public CommandLine() {
|
public CommandLine() : this(new List<Argument>()) { }
|
||||||
this.arguments = new List<Argument>();
|
|
||||||
|
/// <summary>Initializes a new command line</summary>
|
||||||
|
/// <param name="argumentList">List containing the parsed arguments</param>
|
||||||
|
private CommandLine(List<Argument> argumentList) {
|
||||||
|
this.arguments = argumentList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_TOKENIZED_COMMAND_LINE_PARSING // don't enable, it's broken!
|
||||||
|
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||||
|
/// <param name="commandLineArguments">Command line tokens that will be parsed</param>
|
||||||
|
/// <returns>The parsed command line</returns>
|
||||||
|
public static CommandLine Parse(string[] commandLineArguments) {
|
||||||
|
bool windowsMode = (Path.DirectorySeparatorChar != '/');
|
||||||
|
return Parse(commandLineArguments, windowsMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||||
|
/// <param name="commandLineArguments">Command line tokens that will be parsed</param>
|
||||||
|
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||||
|
/// <returns>The parsed command line</returns>
|
||||||
|
public static CommandLine Parse(string[] commandLineArguments, bool windowsMode) {
|
||||||
|
return new CommandLine(
|
||||||
|
Parser.Parse(commandLineArguments, windowsMode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TOKENIZED_COMMAND_LINE_PARSING
|
||||||
|
|
||||||
/// <summary>Parses the command line arguments from the provided string</summary>
|
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||||
/// <returns>The parsed command line</returns>
|
/// <returns>The parsed command line</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// You should always pass Environment.CommandLine to this methods to avoid
|
||||||
|
/// some problems with the build-in command line tokenizer in .NET
|
||||||
|
/// (which splits '--test"hello world"/v' into '--testhello world/v')
|
||||||
|
/// </remarks>
|
||||||
public static CommandLine Parse(string commandLineString) {
|
public static CommandLine Parse(string commandLineString) {
|
||||||
bool windowsMode = (Path.DirectorySeparatorChar != '/');
|
bool windowsMode = (Path.DirectorySeparatorChar != '/');
|
||||||
return Parser.Parse(commandLineString, windowsMode);
|
return Parse(commandLineString, windowsMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Parses the command line arguments from the provided string</summary>
|
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||||
/// <returns>The parsed command line</returns>
|
/// <returns>The parsed command line</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// You should always pass Environment.CommandLine to this methods to avoid
|
||||||
|
/// some problems with the build-in command line tokenizer in .NET
|
||||||
|
/// (which splits '--test"hello world"/v' into '--testhello world/v')
|
||||||
|
/// </remarks>
|
||||||
public static CommandLine Parse(string commandLineString, bool windowsMode) {
|
public static CommandLine Parse(string commandLineString, bool windowsMode) {
|
||||||
return Parser.Parse(commandLineString, windowsMode);
|
return new CommandLine(
|
||||||
}
|
Parser.Parse(commandLineString, windowsMode)
|
||||||
|
|
||||||
#region To Be Removed
|
|
||||||
|
|
||||||
/// <summary>Adds a loose value to the command line</summary>
|
|
||||||
/// <param name="value">Value taht will be added</param>
|
|
||||||
internal void addValue(StringSegment value) {
|
|
||||||
/*
|
|
||||||
Console.WriteLine("Discovered loose value: '" + value.ToString() + "'");
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.arguments.Add(
|
|
||||||
Argument.ValueOnly(value, value.Offset, value.Count)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Adds an argument to the command line</summary>
|
/// <summary>Returns whether an argument with the specified name exists</summary>
|
||||||
/// <param name="argument">Argument that will be added</param>
|
/// <param name="name">Name of the argument whose existence will be checked</param>
|
||||||
internal void addArgument(Argument argument) {
|
/// <returns>True if an argument with the specified name exists</returns>
|
||||||
/*
|
public bool HasArgument(string name) {
|
||||||
Console.WriteLine("Discovered option: '" + argument.Raw.ToString() + "'");
|
return (indexOfArgument(name) != -1);
|
||||||
Console.WriteLine(" Name: '" + argument.Name + "'");
|
}
|
||||||
|
|
||||||
|
#if false
|
||||||
|
/// <summary>Retrieves the value of the specified argument</summary>
|
||||||
|
/// <param name="name">Name of the argument whose value will be retrieved</param>
|
||||||
|
/// <returns>The value of the specified argument</returns>
|
||||||
|
public string GetValue(string name) {
|
||||||
|
int index = indexOfArgument(name);
|
||||||
|
if(index == -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does this argument have a value?
|
||||||
|
Argument argument = this.arguments[index];
|
||||||
if(argument.Value != null) {
|
if(argument.Value != null) {
|
||||||
Console.WriteLine(" Value: '" + argument.Value + "'");
|
return argument.Value;
|
||||||
}
|
} else { // No, it might be a spaced argument
|
||||||
*/
|
|
||||||
|
|
||||||
this.arguments.Add(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
|
||||||
|
|
||||||
|
/// <summary>Retrieves the index of the argument with the specified name</summary>
|
||||||
|
/// <param name="name">Name of the argument whose index will be returned</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The index of the indicated argument of -1 if no argument with that name exists
|
||||||
|
/// </returns>
|
||||||
|
private int indexOfArgument(string name) {
|
||||||
|
for(int index = 0; index < this.arguments.Count; ++index) {
|
||||||
|
if(this.arguments[index].Name == name) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion // To Be Removed
|
|
||||||
|
|
||||||
/// <summary>Options that were specified on the command line</summary>
|
/// <summary>Options that were specified on the command line</summary>
|
||||||
public IList<Argument> Arguments {
|
public IList<Argument> Arguments {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user