Skip to content

Commit

Permalink
Merge pull request #25 from slingamn/escapes.2
Browse files Browse the repository at this point in the history
add support for C hex escapes
  • Loading branch information
slingamn authored Jan 26, 2023
2 parents 78c14ca + 3cf33c9 commit 5fd21c6
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 15 deletions.
1 change: 1 addition & 0 deletions ircdog.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Sending Escapes:
Strikethrough | [[S]] | 0x1e
Underscore | [[U]] | 0x1f
Reset | [[R]] | 0x0f
C hex escape | [[\x??]] | 0x??
---------------------------------
These escapes are only enabled in standard mode (not listening mode),
Expand Down
38 changes: 38 additions & 0 deletions lib/strings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2023 Shivaram Lingamneni <[email protected]>
// released under the ISC license

package lib

import (
"testing"
)

var replacementTestCases = []struct {
input string
output string
}{
{"", ""},
{`a`, "a"},
{`[`, "["},
{`[[`, "[["},
{`[[a]]`, "[[a]]"},
{`a[[CTCP]]b`, "a\x01b"},
{`[[B]]b[[B]]`, "\x02b\x02"},
{`a[[\x67]]b`, "a\x67b"},
{`[[\x67]]`, "\x67"},
{`www[[\x00]]`, "www\x00"},
{`www[[\x0D]]`, "www\x0d"},
{`www[[\xff]]`, "www\xff"},
{`www[[\xFF]]`, "www\xff"},
{`[[notanescape]]`, "[[notanescape]]"},
{`[[[U]]]`, "[\x1f]"},
}

func TestReplaceControlCodes(t *testing.T) {
for _, testCase := range replacementTestCases {
actual := ReplaceControlCodes(testCase.input)
if actual != testCase.output {
t.Errorf("expected `%s` -> %#v, got %#v", testCase.input, []byte(testCase.output), []byte(actual))
}
}
}
66 changes: 51 additions & 15 deletions lib/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@

package lib

import "strings"

var controlCodeReplacements = map[string]string{
"[[CTCP]]": "\x01",
"[[B]]": "\x02",
"[[C]]": "\x03",
"[[M]]": "\x11",
"[[I]]": "\x1d",
"[[S]]": "\x1e",
"[[U]]": "\x1f",
"[[R]]": "\x0f",
}
import (
"regexp"
"strconv"
"strings"
)

// SplitLineIntoParts splits the given IRC line into separate parts.
func SplitLineIntoParts(line string) []string {
Expand Down Expand Up @@ -64,11 +57,54 @@ func SplitLineIntoParts(line string) []string {
return lineParts
}

var (
// e.g., [[\x00]] for \x00, [[\xFF]] or [[\xff]] for \xff
hexEscapeRegex = regexp.MustCompile(`^\[\[\\x[0-9a-fA-F]{2}\]\]`)
)

var controlCodeReplacements = []struct {
escape string
value byte
}{
{"[[CTCP]]", '\x01'},
{"[[B]]", '\x02'},
{"[[C]]", '\x03'},
{"[[M]]", '\x11'},
{"[[I]]", '\x1d'},
{"[[S]]", '\x1e'},
{"[[U]]", '\x1f'},
{"[[R]]", '\x0f'},
}

// ReplaceControlCodes applies our control code replacements to the line.
func ReplaceControlCodes(line string) string {
for k, v := range controlCodeReplacements {
line = strings.Replace(line, k, v, -1)
if idx := strings.Index(line, "[["); idx == -1 {
return line
}

var buf strings.Builder

LineLoop:
for line != "" {
if line[0] == '[' {
for _, replacement := range controlCodeReplacements {
if strings.HasPrefix(line, replacement.escape) {
buf.WriteByte(replacement.value)
line = line[len(replacement.escape):]
continue LineLoop
}
}
if hexEscapeRegex.MatchString(line) {
if val, err := strconv.ParseUint(strings.ToLower(line[4:6]), 16, 8); err == nil {
buf.WriteByte(byte(val))
line = line[8:]
continue LineLoop
}
}
}
buf.WriteByte(line[0])
line = line[1:]
}

return line
return buf.String()
}

0 comments on commit 5fd21c6

Please sign in to comment.