Merge pull request #27 from slingamn/panic.2

fix panic in ircfmt.Unescape
This commit is contained in:
Daniel Oaks 2020-12-28 10:25:32 +10:00 committed by GitHub
commit 4e36cb3f41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 85 deletions

@ -4,6 +4,7 @@
package ircfmt
import (
"regexp"
"strings"
)
@ -81,26 +82,6 @@ var (
"0": "white",
}
// full and truncated colour codes
colourcodesFull = map[string]string{
"white": "00",
"black": "01",
"blue": "02",
"green": "03",
"red": "04",
"brown": "05",
"magenta": "06",
"orange": "07",
"yellow": "08",
"light green": "09",
"cyan": "10",
"light cyan": "11",
"light blue": "12",
"pink": "13",
"grey": "14",
"light grey": "15",
"default": "99",
}
colourcodesTruncated = map[string]string{
"white": "0",
"black": "1",
@ -120,6 +101,9 @@ var (
"light grey": "15",
"default": "99",
}
bracketedExpr = regexp.MustCompile(`^\[.*\]`)
colourDigits = regexp.MustCompile(`^[0-9]{1,2}$`)
)
// Escape takes a raw IRC string and returns it with our escapes.
@ -242,87 +226,90 @@ func removeColour(runes []rune) []rune {
return runes
}
// resolve "light blue" to "12", "12" to "12", "asdf" to "", etc.
func resolveToColourCode(str string) (result string) {
str = strings.ToLower(strings.TrimSpace(str))
if colourDigits.MatchString(str) {
return str
}
return colourcodesTruncated[str]
}
// resolve "[light blue, black]" to ("13, "1")
func resolveToColourCodes(namedColors string) (foreground, background string) {
// cut off the brackets
namedColors = strings.TrimPrefix(namedColors, "[")
namedColors = strings.TrimSuffix(namedColors, "]")
var foregroundStr, backgroundStr string
commaIdx := strings.IndexByte(namedColors, ',')
if commaIdx != -1 {
foregroundStr = namedColors[:commaIdx]
backgroundStr = namedColors[commaIdx+1:]
} else {
foregroundStr = namedColors
}
return resolveToColourCode(foregroundStr), resolveToColourCode(backgroundStr)
}
// Unescape takes our escaped string and returns a raw IRC string.
//
// IE, it turns this: "This is a $bcool$b, $c[red]red$r message!"
// into this: "This is a \x02cool\x02, \x034red\x0f message!"
func Unescape(in string) string {
out := strings.Builder{}
var out strings.Builder
remaining := []rune(in)
for 0 < len(remaining) {
remaining := in
for len(remaining) != 0 {
char := remaining[0]
remaining = remaining[1:]
if char == '$' && 0 < len(remaining) {
char = remaining[0]
remaining = remaining[1:]
if char != '$' || len(remaining) == 0 {
// not an escape
out.WriteByte(char)
continue
}
val, exists := escapetoval[char]
if exists {
out.WriteString(val)
} else if char == 'c' {
out.WriteString(colour)
// ingest the next character of the escape
char = remaining[0]
remaining = remaining[1:]
if len(remaining) < 2 || remaining[0] != '[' {
continue
if char == 'c' {
out.WriteString(colour)
namedColors := bracketedExpr.FindString(remaining)
if namedColors == "" {
// for a non-bracketed color code, output the following characters directly,
// e.g., `$c1,8` will become `\x031,8`
continue
}
// process bracketed color codes:
remaining = remaining[len(namedColors):]
followedByDigit := len(remaining) != 0 && ('0' <= remaining[0] && remaining[0] <= '9')
foreground, background := resolveToColourCodes(namedColors)
if foreground != "" {
if len(foreground) == 1 && background == "" && followedByDigit {
out.WriteByte('0')
}
// get colour names
var coloursBuffer string
remaining = remaining[1:]
for remaining[0] != ']' {
coloursBuffer += string(remaining[0])
remaining = remaining[1:]
}
remaining = remaining[1:] // strip final ']'
colours := strings.Split(coloursBuffer, ",")
var foreColour, backColour string
foreColour = colours[0]
if 1 < len(colours) {
backColour = colours[1]
}
// decide whether we can use truncated colour codes
canUseTruncated := len(remaining) < 1 || !strings.Contains(colours1, string(remaining[0]))
// turn colour names into real codes
var foreColourCode, backColourCode string
var exists bool
if backColour != "" || canUseTruncated {
foreColourCode, exists = colourcodesTruncated[foreColour]
} else {
foreColourCode, exists = colourcodesFull[foreColour]
}
if exists {
foreColour = foreColourCode
}
if backColour != "" {
if canUseTruncated {
backColourCode, exists = colourcodesTruncated[backColour]
} else {
backColourCode, exists = colourcodesFull[backColour]
}
if exists {
backColour = backColourCode
out.WriteString(foreground)
if background != "" {
out.WriteByte(',')
if len(background) == 1 && followedByDigit {
out.WriteByte('0')
}
out.WriteString(background)
}
// output colour codes
out.WriteString(foreColour)
if backColour != "" {
out.WriteRune(',')
out.WriteString(backColour)
}
} else {
// unknown char
out.WriteRune(char)
}
} else {
out.WriteRune(char)
val, exists := escapetoval[rune(char)]
if exists {
out.WriteString(val)
} else {
// invalid escape, use the raw char
out.WriteByte(char)
}
}
}

@ -28,6 +28,16 @@ var unescapetests = []testcase{
{"te$xt", "text"},
{"te$st", "te\x1et"},
{"test$c", "test\x03"},
{"te$c[red velvet", "te\x03[red velvet"},
{"te$c[[red velvet", "te\x03[[red velvet"},
{"test$c[light blue,black]asdf", "test\x0312,1asdf"},
{"test$c[light blue, black]asdf", "test\x0312,1asdf"},
{"te$c[4,3]st", "te\x034,3st"},
{"te$c[4]1st", "te\x03041st"},
{"te$c[4,3]9st", "te\x034,039st"},
{"te$c[04,03]9st", "te\x0304,039st"},
{"te$c[asdf !23a fd4*#]st", "te\x03st"},
{"te$c[asdf , !2,3a fd4*#]st", "te\x03st"},
}
var stripTests = []testcase{