diff --git a/Source/Parsing/CommandLine.Test.cs b/Source/Parsing/CommandLine.Test.cs
index 3e4e440..375747a 100644
--- a/Source/Parsing/CommandLine.Test.cs
+++ b/Source/Parsing/CommandLine.Test.cs
@@ -584,7 +584,7 @@ namespace Nuclex.Support.Parsing {
///
[Test]
public void TestCommandLineFormatting() {
- CommandLine commandLine = new CommandLine();
+ CommandLine commandLine = new CommandLine(true);
commandLine.AddValue("single");
commandLine.AddValue("with space");
@@ -623,6 +623,23 @@ namespace Nuclex.Support.Parsing {
}
+ ///
+ /// Tests whether a command line can be built that contains empty arguments
+ ///
+ [Test]
+ public void TestNullArgumentFormatting() {
+ CommandLine commandLine = new CommandLine(false);
+
+ commandLine.AddValue(string.Empty);
+ commandLine.AddValue("hello");
+ commandLine.AddValue(null);
+ commandLine.AddValue("-test");
+
+ Assert.AreEqual(4, commandLine.Arguments.Count);
+ string commandLineString = commandLine.ToString();
+ Assert.AreEqual("\"\" hello \"\" \"-test\"", commandLineString);
+ }
+
}
} // namespace Nuclex.Support.Parsing
diff --git a/Source/Parsing/CommandLine.cs b/Source/Parsing/CommandLine.cs
index 0e9d45a..65fc48f 100644
--- a/Source/Parsing/CommandLine.cs
+++ b/Source/Parsing/CommandLine.cs
@@ -82,21 +82,40 @@ namespace Nuclex.Support.Parsing {
///
public partial class CommandLine {
+ ///
+ /// Whether the command line should use Windows mode by default
+ ///
+ public static readonly bool WindowsModeDefault =
+ (Path.DirectorySeparatorChar == '\\');
+
/// Initializes a new command line
- public CommandLine() : this(new List()) { }
+ public CommandLine() :
+ this(new List(), WindowsModeDefault) { }
+
+ /// Initializes a new command line
+ /// Whether the / character initiates an argument
+ public CommandLine(bool windowsMode) :
+ this(new List(), windowsMode) { }
/// Initializes a new command line
/// List containing the parsed arguments
- private CommandLine(List argumentList) {
+ private CommandLine(List argumentList) :
+ this(argumentList, WindowsModeDefault) { }
+
+ /// Initializes a new command line
+ /// List containing the parsed arguments
+ /// Whether the / character initiates an argument
+ private CommandLine(List argumentList, bool windowsMode) {
this.arguments = argumentList;
+ this.windowsMode = windowsMode;
}
/// Parses the command line arguments from the provided string
/// String containing the command line arguments
/// The parsed command line
///
- /// You should always pass Environment.CommandLine to this methods to avoid
- /// some problems with the build-in command line tokenizer in .NET
+ /// You should always pass Environment.CommandLine to this method to avoid
+ /// some problems with the built-in command line tokenizer in .NET
/// (which splits '--test"hello world"/v' into '--testhello world/v')
///
public static CommandLine Parse(string commandLineString) {
@@ -110,7 +129,7 @@ namespace Nuclex.Support.Parsing {
/// The parsed command line
///
/// You should always pass Environment.CommandLine to this methods to avoid
- /// some problems with the build-in command line tokenizer in .NET
+ /// some problems with the built-in command line tokenizer in .NET
/// (which splits '--test"hello world"/v' into '--testhello world/v')
///
public static CommandLine Parse(string commandLineString, bool windowsMode) {
@@ -129,24 +148,24 @@ namespace Nuclex.Support.Parsing {
/// 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);
+ int valueLength = (value != null) ? value.Length : 0;
- if(valueContainsSpaces) {
- StringBuilder builder = new StringBuilder(value.Length + 2);
+ if(requiresQuotes(value)) {
+ StringBuilder builder = new StringBuilder(valueLength + 2);
builder.Append('"');
builder.Append(value);
builder.Append('"');
this.arguments.Add(
Argument.ValueOnly(
- new StringSegment(builder.ToString(), 0, value.Length + 2),
+ new StringSegment(builder.ToString(), 0, valueLength + 2),
1,
- value.Length
+ valueLength
)
);
} else {
this.arguments.Add(
- Argument.ValueOnly(new StringSegment(value), 0, value.Length)
+ Argument.ValueOnly(new StringSegment(value), 0, valueLength)
);
}
}
@@ -161,9 +180,7 @@ namespace Nuclex.Support.Parsing {
/// 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
- );
+ StringBuilder builder = new StringBuilder(initiator.Length + name.Length);
builder.Append(initiator);
builder.Append(name);
@@ -188,10 +205,9 @@ namespace Nuclex.Support.Parsing {
/// 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);
+ bool valueContainsSpaces = containsWhitespace(value);
StringBuilder builder = new StringBuilder(
- initiator.Length + name.Length + 1 + value.Length +
- (valueContainsSpaces ? 2 : 0)
+ initiator.Length + name.Length + 1 + value.Length + (valueContainsSpaces ? 2 : 0)
);
builder.Append(initiator);
builder.Append(name);
@@ -209,8 +225,7 @@ namespace Nuclex.Support.Parsing {
new StringSegment(builder.ToString()),
initiator.Length,
name.Length,
- initiator.Length + name.Length + 1 +
- (valueContainsSpaces ? 1 : 0),
+ initiator.Length + name.Length + 1 + (valueContainsSpaces ? 1 : 0),
value.Length
)
);
@@ -242,8 +257,50 @@ namespace Nuclex.Support.Parsing {
get { return this.arguments; }
}
+ ///
+ /// Determines whether the string requires quotes to survive the command line
+ ///
+ /// Value that will be checked for requiring quotes
+ /// True if the value requires quotes to survive the command line
+ private bool requiresQuotes(string value) {
+
+ // If the value is empty, it needs quotes to become visible as an argument
+ // (versus being intepreted as spacing between other arguments)
+ if(string.IsNullOrEmpty(value)) {
+ return true;
+ }
+
+ // Any whitespace characters force us to use quotes, so does a minus sign
+ // at the beginning of the value (otherwise, it would become an option argument)
+ bool requiresQuotes =
+ containsWhitespace(value) ||
+ (value[0] == '-');
+
+ // On windows, option arguments can also be starten with the forward slash
+ // character, so we require quotes as well if the value starts with one
+ if(this.windowsMode) {
+ requiresQuotes |= (value[0] == '/');
+ }
+
+ return requiresQuotes;
+
+ }
+
+ ///
+ /// Determines whether the string contains any whitespace characters
+ ///
+ /// String that will be scanned for whitespace characters
+ /// True if the provided string contains whitespace characters
+ private static bool containsWhitespace(string value) {
+ return
+ (value.IndexOf(' ') != -1) ||
+ (value.IndexOf('\t') != -1);
+ }
+
/// Options that were specified on the command line
private List arguments;
+ /// Whether the / character initiates an argument
+ private bool windowsMode;
}
diff --git a/Source/Plugins/PluginHost.cs b/Source/Plugins/PluginHost.cs
index 7e2cda9..395d4e9 100644
--- a/Source/Plugins/PluginHost.cs
+++ b/Source/Plugins/PluginHost.cs
@@ -101,6 +101,7 @@ namespace Nuclex.Support.Plugins {
Trace.WriteLine("Could not employ " + type.ToString() + ": " + exception.Message);
}
}
+
}
///
diff --git a/Source/Scheduling/AbortedException.cs b/Source/Scheduling/AbortedException.cs
index 30f8dda..993f334 100644
--- a/Source/Scheduling/AbortedException.cs
+++ b/Source/Scheduling/AbortedException.cs
@@ -43,18 +43,14 @@ namespace Nuclex.Support.Scheduling {
/// Preceding exception that has caused this exception
public AbortedException(string message, Exception inner) : base(message, inner) { }
-#if !COMPACTFRAMEWORK
-
/// Initializes the exception from its serialized state
/// Contains the serialized fields of the exception
/// Additional environmental informations
protected AbortedException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context
- )
- : base(info, context) { }
-
-#endif // !COMPACTFRAMEWORK
+ ) :
+ base(info, context) { }
}
diff --git a/Source/Scheduling/GenericTimeSource.cs b/Source/Scheduling/GenericTimeSource.cs
index 47acab5..db27ec8 100644
--- a/Source/Scheduling/GenericTimeSource.cs
+++ b/Source/Scheduling/GenericTimeSource.cs
@@ -77,6 +77,7 @@ namespace Nuclex.Support.Scheduling {
public GenericTimeSource(bool useStopwatch) {
this.useStopwatch = useStopwatch;
+ // Update the lastCheckedTime and lastCheckedTicks fields
checkForTimeAdjustment();
}
@@ -96,7 +97,7 @@ namespace Nuclex.Support.Scheduling {
// See whether the system date/time have been adjusted while we were asleep.
checkForTimeAdjustment();
- // Now tell the caller whether his even was signalled
+ // Now tell the caller whether his event was signalled
return signalled;
}
diff --git a/Source/Scheduling/ITimeSource.cs b/Source/Scheduling/ITimeSource.cs
index beee0b7..d674b23 100644
--- a/Source/Scheduling/ITimeSource.cs
+++ b/Source/Scheduling/ITimeSource.cs
@@ -44,7 +44,7 @@ namespace Nuclex.Support.Scheduling {
///
///
/// Depending on whether the system will provide notifications when date/time
- /// is adjusted, the time source will be forced to let thid method block for
+ /// is adjusted, the time source will be forced to let this method block for
/// less than the indicated time before returning a timeout in order to give
/// the caller a chance to recheck the system time.
///