tangled
alpha
login
or
join now
benharri.org
/
ircsharp
1
fork
atom
IRC parsing, tokenization, and state handling in C#
1
fork
atom
overview
issues
pulls
pipelines
move tag escape to its own file
benharri.org
3 months ago
6f8e62e9
99e10b9a
+68
-64
2 changed files
expand all
collapse all
unified
split
IRCTokens
Line.cs
TagEscape.cs
+5
-64
IRCTokens/Line.cs
···
1
1
-
using System.Globalization;
2
2
-
using System.Text;
3
3
-
4
1
// ReSharper disable ReplaceWithFieldKeyword
5
2
// ReSharper disable CommentTypo
6
3
···
9
6
/// <summary>Tools to represent, parse, and format IRC lines</summary>
10
7
public class Line : IEquatable<Line>
11
8
{
12
12
-
private static readonly string[] TagUnescaped = ["\\", " ", ";", "\r", "\n"];
13
13
-
14
14
-
private static readonly string[] TagEscaped = [@"\\", "\\s", "\\:", "\\r", "\\n"];
15
9
private Hostmask _hostmask;
16
10
17
11
public Line()
···
48
42
if (part.Contains('='))
49
43
{
50
44
split = part.Split(['='], 2);
51
51
-
Tags[split[0]] = UnescapeTag(split[1]);
45
45
+
Tags[split[0]] = TagEscape.Unescape(split[1]);
52
46
}
53
47
else
54
48
{
···
96
90
public string Command { get; set; }
97
91
public List<string> Params { get; set; }
98
92
99
99
-
public Hostmask Hostmask => _hostmask ??= new Hostmask(Source);
93
93
+
public Hostmask Hostmask => _hostmask ??= new(Source);
100
94
101
95
public bool Equals(Line other)
102
96
{
···
108
102
return Format() == other.Format();
109
103
}
110
104
111
111
-
/// <summary>Unescape ircv3 tag</summary>
112
112
-
/// <param name="val">escaped string</param>
113
113
-
/// <returns>unescaped string</returns>
114
114
-
private static string UnescapeTag(string val)
115
115
-
{
116
116
-
var unescaped = new StringBuilder();
117
117
-
118
118
-
var graphemeIterator = StringInfo.GetTextElementEnumerator(val);
119
119
-
graphemeIterator.Reset();
120
120
-
121
121
-
while (graphemeIterator.MoveNext())
122
122
-
{
123
123
-
var current = graphemeIterator.GetTextElement();
124
124
-
125
125
-
if (current == @"\")
126
126
-
{
127
127
-
try
128
128
-
{
129
129
-
graphemeIterator.MoveNext();
130
130
-
var next = graphemeIterator.GetTextElement();
131
131
-
var pair = current + next;
132
132
-
unescaped.Append(TagEscaped.Contains(pair)
133
133
-
? TagUnescaped[Array.IndexOf(TagEscaped, pair)]
134
134
-
: next);
135
135
-
}
136
136
-
catch (InvalidOperationException)
137
137
-
{
138
138
-
// ignored
139
139
-
}
140
140
-
}
141
141
-
else
142
142
-
{
143
143
-
unescaped.Append(current);
144
144
-
}
145
145
-
}
146
146
-
147
147
-
return unescaped.ToString();
148
148
-
}
149
149
-
150
150
-
/// <summary>Escape strings for use in ircv3 tags</summary>
151
151
-
/// <param name="val">string to escape</param>
152
152
-
/// <returns>escaped string</returns>
153
153
-
private static string EscapeTag(string val)
154
154
-
{
155
155
-
StringBuilder sb = new(val);
156
156
-
for (var i = 0; i < TagUnescaped.Length; ++i)
157
157
-
{
158
158
-
sb.Replace(TagUnescaped[i], TagEscaped[i]);
159
159
-
}
160
160
-
161
161
-
return sb.ToString();
162
162
-
}
163
163
-
164
105
public override string ToString()
165
106
{
166
166
-
var vars = new List<string>();
107
107
+
List<string> vars = [];
167
108
168
109
if (Command != null)
169
110
{
···
196
137
/// <returns>formatted irc line</returns>
197
138
public string Format()
198
139
{
199
199
-
var outs = new List<string>();
140
140
+
List<string> outs = [];
200
141
201
142
if (Tags != null && Tags.Any())
202
143
{
203
144
var tags = Tags.Keys
204
145
.OrderBy(k => k)
205
205
-
.Select(key => string.IsNullOrWhiteSpace(Tags[key]) ? key : $"{key}={EscapeTag(Tags[key])}")
146
146
+
.Select(key => string.IsNullOrWhiteSpace(Tags[key]) ? key : $"{key}={TagEscape.Escape(Tags[key])}")
206
147
.ToList();
207
148
208
149
outs.Add($"@{string.Join(";", tags)}");
+63
IRCTokens/TagEscape.cs
···
1
1
+
using System.Globalization;
2
2
+
using System.Text;
3
3
+
4
4
+
namespace IRCTokens;
5
5
+
6
6
+
public static class TagEscape
7
7
+
{
8
8
+
private static readonly string[] TagUnescaped = ["\\", " ", ";", "\r", "\n"];
9
9
+
private static readonly string[] TagEscaped = [@"\\", "\\s", "\\:", "\\r", "\\n"];
10
10
+
11
11
+
/// <summary>Unescape ircv3 tag</summary>
12
12
+
/// <param name="val">escaped string</param>
13
13
+
/// <returns>unescaped string</returns>
14
14
+
public static string Unescape(string val)
15
15
+
{
16
16
+
StringBuilder unescaped = new();
17
17
+
18
18
+
var graphemeIterator = StringInfo.GetTextElementEnumerator(val);
19
19
+
graphemeIterator.Reset();
20
20
+
21
21
+
while (graphemeIterator.MoveNext())
22
22
+
{
23
23
+
var current = graphemeIterator.GetTextElement();
24
24
+
25
25
+
if (current == @"\")
26
26
+
{
27
27
+
try
28
28
+
{
29
29
+
graphemeIterator.MoveNext();
30
30
+
var next = graphemeIterator.GetTextElement();
31
31
+
var pair = current + next;
32
32
+
unescaped.Append(TagEscaped.Contains(pair)
33
33
+
? TagUnescaped[Array.IndexOf(TagEscaped, pair)]
34
34
+
: next);
35
35
+
}
36
36
+
catch (InvalidOperationException)
37
37
+
{
38
38
+
// ignored
39
39
+
}
40
40
+
}
41
41
+
else
42
42
+
{
43
43
+
unescaped.Append(current);
44
44
+
}
45
45
+
}
46
46
+
47
47
+
return unescaped.ToString();
48
48
+
}
49
49
+
50
50
+
/// <summary>Escape strings for use in ircv3 tags</summary>
51
51
+
/// <param name="val">string to escape</param>
52
52
+
/// <returns>escaped string</returns>
53
53
+
public static string Escape(string val)
54
54
+
{
55
55
+
StringBuilder sb = new(val);
56
56
+
for (var i = 0; i < TagUnescaped.Length; ++i)
57
57
+
{
58
58
+
sb.Replace(TagUnescaped[i], TagEscaped[i]);
59
59
+
}
60
60
+
61
61
+
return sb.ToString();
62
62
+
}
63
63
+
}