diff --git a/Source/Settings/ConfigurationFileStore.Parsing.cs b/Source/Settings/ConfigurationFileStore.Parsing.cs
index 4d537a2..6500b89 100644
--- a/Source/Settings/ConfigurationFileStore.Parsing.cs
+++ b/Source/Settings/ConfigurationFileStore.Parsing.cs
@@ -116,7 +116,7 @@ namespace Nuclex.Support.Settings {
if(nameStartIndex >= lastCharacterIndex) {
return; // No space left for closing brace
}
-
+
int nameEndIndex = line.IndexOf(']', nameStartIndex);
if(nameEndIndex == -1) {
return; // No closing brace in line
@@ -163,15 +163,10 @@ namespace Nuclex.Support.Settings {
LineIndex = state.Store.lines.Count - 1,
OptionName = new StringSegment(
line, firstCharacterIndex, nameEndIndex - firstCharacterIndex + 1
- ),
+ )
};
- // If there is a value in this assignment, parse it too
- int valueStartIndex = assignmentIndex + 1;
- ParserHelper.SkipSpaces(line, ref valueStartIndex);
- if(valueStartIndex < line.Length) {
- parseOptionValue(option, line, valueStartIndex);
- }
+ parseOptionValue(option, line, assignmentIndex + 1);
// We've got the option assignment, either with an empty or proper value
state.Store.options.Add(option);
@@ -181,9 +176,54 @@ namespace Nuclex.Support.Settings {
/// Parses the value assigned to an option
/// Option to which a value is being assigned
/// Line containing the option assignment
- /// Index of the value's first character
- private static void parseOptionValue(Option option, string line, int valueStartIndex) {
-
+ /// Index one after the assignment character
+ private static void parseOptionValue(Option option, string line, int assignmentEndIndex) {
+ int firstCharacterIndex = assignmentEndIndex;
+ ParserHelper.SkipSpaces(line, ref firstCharacterIndex);
+
+ // Just for beauty, when the option value is empty but padded with spaces,
+ // leave one space between the equals sign and the value.
+ if(firstCharacterIndex > assignmentEndIndex) {
+ ++assignmentEndIndex;
+ }
+
+ // If the line consists of only whitespace, create an empty value
+ if(firstCharacterIndex == line.Length) {
+ option.OptionValue = new StringSegment(line, assignmentEndIndex, 0);
+ return;
+ }
+
+ char firstCharacter = line[firstCharacterIndex];
+
+ // Values can be quoted to allow for comments characters appearing in them
+ int lastCharacterIndex;
+ if(firstCharacter == '"') {
+ lastCharacterIndex = line.LastIndexOf('"');
+ } else {
+ lastCharacterIndex = firstCharacterIndex;
+ }
+
+ int commentStartIndex = line.IndexOf(';', lastCharacterIndex);
+ if(commentStartIndex == -1) {
+ commentStartIndex = line.IndexOf('#', lastCharacterIndex);
+ }
+ if(commentStartIndex == -1) {
+ lastCharacterIndex = line.Length - 1;
+ } else {
+ lastCharacterIndex = commentStartIndex - 1;
+ }
+
+ while(lastCharacterIndex > firstCharacterIndex) {
+ if(char.IsWhiteSpace(line, lastCharacterIndex)) {
+ --lastCharacterIndex;
+ } else {
+ break;
+ }
+ }
+
+ option.OptionValue = new StringSegment(
+ line, firstCharacterIndex, lastCharacterIndex - firstCharacterIndex + 1
+ );
}
/// Determines the best matching type for an option value
diff --git a/Source/Settings/ConfigurationFileStore.Test.cs b/Source/Settings/ConfigurationFileStore.Test.cs
index db96786..4d36eaf 100644
--- a/Source/Settings/ConfigurationFileStore.Test.cs
+++ b/Source/Settings/ConfigurationFileStore.Test.cs
@@ -21,10 +21,10 @@ License along with this library
#if UNITTEST
using System;
+using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
-using System.Collections.Generic;
namespace Nuclex.Support.Settings {
@@ -32,6 +32,9 @@ namespace Nuclex.Support.Settings {
[TestFixture]
internal class ConfigurationFileStoreTest {
+ /// Loads a configuration file from a string
+ /// Contents of the configuration file
+ /// The configuration file loaded from the string
private static ConfigurationFileStore load(string fileContents) {
using(var reader = new StringReader(fileContents)) {
return ConfigurationFileStore.Parse(reader);
@@ -128,11 +131,84 @@ namespace Nuclex.Support.Settings {
for(int index = 0; index < options.Count; ++index) {
Assert.That(
- configurationFile.Get(null, options[index].Name), Is.Null
+ configurationFile.Get(null, options[index].Name), Is.Null.Or.Empty
);
}
}
+ ///
+ /// Verifies that values assigned to options can contain space charcters
+ ///
+ [Test]
+ public void OptionValuesCanContainSpaces() {
+ string fileContents =
+ "test = hello world";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ Assert.That(configurationFile.Get(null, "test"), Is.EqualTo("hello world"));
+ }
+
+ ///
+ /// Verifies that values enclosed in quotes can embed comment characters
+ ///
+ [Test]
+ public void OptionValuesWithQuotesCanEmbedComments() {
+ string fileContents =
+ "test = \"This ; is # not a comment\" # but this is";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ Assert.That(
+ configurationFile.Get(null, "test"),
+ Is.EqualTo("\"This ; is # not a comment\"")
+ );
+ }
+
+ ///
+ /// Verifies that values can end on a quote without causing trouble
+ ///
+ [Test]
+ public void CommentsCanEndWithAQuote() {
+ string fileContents =
+ "test = \"This value ends with a quote\"";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ Assert.That(
+ configurationFile.Get(null, "test"),
+ Is.EqualTo("\"This value ends with a quote\"")
+ );
+ }
+
+ ///
+ /// Verifies that values can forget the closing quote without causing trouble
+ ///
+ [Test]
+ public void ClosingQuoteCanBeOmmitted() {
+ string fileContents =
+ "test = \"No closing quote";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ Assert.That(
+ configurationFile.Get(null, "test"),
+ Is.EqualTo("\"No closing quote")
+ );
+ }
+
+ ///
+ /// Verifies that text placed after the closing quote will also be part of
+ /// an option's value
+ ///
+ [Test]
+ public void TextAfterClosingQuoteBecomesPartOfValue() {
+ string fileContents =
+ "test = \"Begins here\" end ends here";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ Assert.That(
+ configurationFile.Get(null, "test"),
+ Is.EqualTo("\"Begins here\" end ends here")
+ );
+ }
+
}
} // namespace Nuclex.Support.Settings
diff --git a/Source/Settings/ConfigurationFileStore.cs b/Source/Settings/ConfigurationFileStore.cs
index 3e0bb0f..fa15b6d 100644
--- a/Source/Settings/ConfigurationFileStore.cs
+++ b/Source/Settings/ConfigurationFileStore.cs
@@ -21,8 +21,6 @@ License along with this library
using System;
using System.Collections.Generic;
using System.IO;
-
-using Nuclex.Support.Parsing;
using System.Text;
namespace Nuclex.Support.Settings {