Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Escaping and unescaping new line characters #143

Merged
merged 4 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions src/Config.Net.Tests/Stores/Formats/IniKeyValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ namespace Config.Net.Tests.Stores.Formats
public class IniKeyValueTest
{
[Theory]
[InlineData("key=value", "key", "value")]
[InlineData("key=value;123", "key", "value")]
[InlineData("key==value", "key", "=value")]
[InlineData("key=value;value;value", "key", "value;value")]
[InlineData("key=value=value;value", "key", "value=value")]
public void FromLine_ParsingInlineComments(string input, string expectedKey, string expectedValue)
[InlineData("key=value", "key", "value", null)]
[InlineData("key=value;123", "key", "value", "123")]
[InlineData("key==value", "key", "=value", null)]
[InlineData("key=value;value;value", "key", "value;value", "value")]
[InlineData("key=value=value;value", "key", "value=value", "value")]
[InlineData("key=value;rest;", "key", "value;rest", "")]
public void FromLine_ParsingInlineComments(string input, string expectedKey, string expectedValue, string expectedComment)
{
IniKeyValue kv = IniKeyValue.FromLine(input, true);

Assert.Equal(expectedKey, kv.Key);
Assert.Equal(expectedValue, kv.Value);
FromLine_DoTest(true, false, input, expectedKey, expectedValue, expectedComment);
}

[Theory]
Expand All @@ -27,11 +25,32 @@ public void FromLine_ParsingInlineComments(string input, string expectedKey, str
[InlineData("key=value=value;value", "key", "value=value;value")]
public void FromLine_IgnoringInlineComments(string input, string expectedKey, string expectedValue)
{
IniKeyValue kv = IniKeyValue.FromLine(input, false);
FromLine_DoTest(false, false, input, expectedKey, expectedValue, null);
}

[Theory]
[InlineData(true, @"k1\r\nk2=v1\r\nv2;c1\r\nc2", "k1\r\nk2", "v1\r\nv2", "c1\r\nc2")]
[InlineData(false, @"k1\r\nk2=v1\r\nv2;c1\r\nc2", @"k1\r\nk2", @"v1\r\nv2", @"c1\r\nc2")]
public void FromLine_UnescapeNewLines(bool unescapeNewLines, string input, string expectedKey, string expectedValue, string expectedComment)
{
FromLine_DoTest(true, unescapeNewLines, input, expectedKey, expectedValue, expectedComment);
}

private static void FromLine_DoTest(bool parseInlineComments, bool unescapeNewLines, string input, string expectedKey, string expectedValue, string expectedComment)
{
IniKeyValue kv = IniKeyValue.FromLine(input, parseInlineComments, unescapeNewLines);

Assert.Equal(expectedKey, kv.Key);
Assert.Equal(expectedValue, kv.Value);
}

if (expectedComment == null)
{
Assert.Null(kv.Comment);
}
else
{
Assert.Equal(expectedComment, kv.Comment.Value);
}
}
}
}
33 changes: 33 additions & 0 deletions src/Config.Net.Tests/Stores/Formats/StructuredIniFileTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.IO;
using System.Text;
using Config.Net.Stores.Formats.Ini;
using Xunit;

namespace Config.Net.Tests.Stores.Formats
{
public class StructuredIniFileTest : AbstractTestFixture
{
[Fact]
public void WriteInlineCommentWithNewLine()
{
string fullPath = Path.Combine(TestDir.FullName, Guid.NewGuid() + ".ini");
string content = @"key=value ;c1\r\nc2
";

StructuredIniFile file;
using (Stream input = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
file = StructuredIniFile.ReadFrom(input, true, true);
}

using (Stream output = File.OpenWrite(fullPath))
{
file.WriteTo(output);
}

string resultText = File.ReadAllText(fullPath);
Assert.Equal(content, resultText);
}
}
}
18 changes: 18 additions & 0 deletions src/Config.Net.Tests/Stores/IniFileConfigStoreTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,23 @@ public void Write_NewFileWithNewValues_WritesCorrectText()
key0=s2value0
", resultText, false, true);
}

[Theory]
[InlineData("key", "l1\r\nl2", @"key=l1\r\nl2")]
[InlineData("key", "l1\rl2", @"key=l1\rl2")]
[InlineData("key", "l1\nl2", @"key=l1\nl2")]
[InlineData("k1\r\nk2", "value", @"k1\r\nk2=value")]
[InlineData("k1\rk2", "value", @"k1\rk2=value")]
[InlineData("k1\nk2", "value", @"k1\nk2=value")]
public void Write_NewLine(string key, string value, string expectedOutput)
{
string fullPath = Path.Combine(TestDir.FullName, Guid.NewGuid() + ".ini");
var ini = new IniFileConfigStore(fullPath, true, true);

ini.Write(key, value);

string resultText = File.ReadAllText(fullPath);
Assert.Equal(expectedOutput + "\r\n", resultText);
}
}
}
5 changes: 5 additions & 0 deletions src/Config.Net/Stores/Formats/Ini/IniComment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public IniComment(string value)

public string Value { get; set; }

public string EscapedValue
{
get { return Value.Replace("\r", @"\r").Replace("\n", @"\n"); }
}

public override string ToString() => Value;
}
}
24 changes: 23 additions & 1 deletion src/Config.Net/Stores/Formats/Ini/IniKeyValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,19 @@ public IniKeyValue(string key, string value, string? comment)

public string Value { get; set; }

public string EscapedKey
{
get { return Key.Replace("\r", @"\r").Replace("\n", @"\n"); }
}

public string EscapedValue
{
get { return Value.Replace("\r", @"\r").Replace("\n", @"\n"); }
}

public IniComment? Comment { get; }

public static IniKeyValue? FromLine(string line, bool parseInlineComments)
public static IniKeyValue? FromLine(string line, bool parseInlineComments, bool unescapeNewLines = false)
{
int idx = line.IndexOf(KeyValueSeparator, StringComparison.CurrentCulture);
if(idx == -1) return null;
Expand All @@ -39,9 +49,21 @@ public IniKeyValue(string key, string value, string? comment)
}
}

if(unescapeNewLines)
{
key = UnescapeString(key);
value = UnescapeString(value);
comment = (comment != null) ? UnescapeString(comment) : null;
}

return new IniKeyValue(key, value, comment);
}

private static string UnescapeString(string key)
{
return key.Replace(@"\r", "\r").Replace(@"\n", "\n");
}

public override string ToString()
{
return $"{Value}";
Expand Down
4 changes: 2 additions & 2 deletions src/Config.Net/Stores/Formats/Ini/IniSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ public void WriteTo(StreamWriter writer)
IniKeyValue? ikv = entity as IniKeyValue;
if(ikv != null)
{
writer.Write($"{ikv.Key}{IniKeyValue.KeyValueSeparator}{ikv.Value}");
writer.Write($"{ikv.EscapedKey}{IniKeyValue.KeyValueSeparator}{ikv.EscapedValue}");
if(ikv.Comment != null)
{
writer.Write(" ");
writer.Write(IniComment.CommentSeparator);
writer.Write(ikv.Comment.Value);
writer.Write(ikv.Comment.EscapedValue);
}
writer.WriteLine();
continue;
Expand Down
4 changes: 2 additions & 2 deletions src/Config.Net/Stores/Formats/Ini/StructuredIniFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public string? this[string key]
}
}

public static StructuredIniFile ReadFrom(Stream inputStream, bool parseInlineComments)
public static StructuredIniFile ReadFrom(Stream inputStream, bool parseInlineComments, bool unescapeNewLines = false)
{
if(inputStream == null) throw new ArgumentNullException(nameof(inputStream));

Expand Down Expand Up @@ -90,7 +90,7 @@ public static StructuredIniFile ReadFrom(Stream inputStream, bool parseInlineCom
}
else
{
IniKeyValue? ikv = IniKeyValue.FromLine(line, parseInlineComments);
IniKeyValue? ikv = IniKeyValue.FromLine(line, parseInlineComments, unescapeNewLines);
if(ikv == null) continue;

section.Add(ikv);
Expand Down
14 changes: 7 additions & 7 deletions src/Config.Net/Stores/IniFileConfigStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class IniFileConfigStore : IConfigStore
/// </summary>r
/// <param name="name">File does not have to exist, however it will be created as soon as you
/// try to write to it</param>
public IniFileConfigStore(string name, bool isFilePath, bool parseInlineComments)
public IniFileConfigStore(string name, bool isFilePath, bool parseInlineComments, bool unescapeNewLines = false)
{
if (name == null) throw new ArgumentNullException(nameof(name));

Expand All @@ -34,13 +34,13 @@ public IniFileConfigStore(string name, bool isFilePath, bool parseInlineComments
Directory.CreateDirectory(parentDirPath);
}

_iniFile = ReadIniFile(_fullName, parseInlineComments);
_iniFile = ReadIniFile(_fullName, parseInlineComments, unescapeNewLines);

CanWrite = true;
}
else
{
_iniFile = ReadIniContent(name, parseInlineComments);
_iniFile = ReadIniContent(name, parseInlineComments, unescapeNewLines);

CanWrite = false;
}
Expand Down Expand Up @@ -78,14 +78,14 @@ public void Write(string key, string? value)
WriteIniFile();
}

private static StructuredIniFile ReadIniFile(string fullName, bool parseInlineComments)
private static StructuredIniFile ReadIniFile(string fullName, bool parseInlineComments, bool unescapeNewLines = false)
{
FileInfo iniFile = new FileInfo(fullName);
if(iniFile.Exists)
{
using(FileStream stream = iniFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return StructuredIniFile.ReadFrom(stream, parseInlineComments);
return StructuredIniFile.ReadFrom(stream, parseInlineComments, unescapeNewLines);
}
}
else
Expand All @@ -94,11 +94,11 @@ private static StructuredIniFile ReadIniFile(string fullName, bool parseInlineCo
}
}

private static StructuredIniFile ReadIniContent(string content, bool parseInlineComments)
private static StructuredIniFile ReadIniContent(string content, bool parseInlineComments, bool unescapeNewLines = false)
{
using (Stream input = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
return StructuredIniFile.ReadFrom(input, parseInlineComments);
return StructuredIniFile.ReadFrom(input, parseInlineComments, unescapeNewLines);
}
}

Expand Down