aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ccl.go20
-rw-r--r--ccl_test.go14
2 files changed, 30 insertions, 4 deletions
diff --git a/ccl.go b/ccl.go
index 1333119..92a1a3f 100644
--- a/ccl.go
+++ b/ccl.go
@@ -42,9 +42,9 @@
//
// # Strings
//
-// Strings are written with " or ' and any sequence of intermediate bytes (with
-// the exception of escape sequences which are described below). Strings must be
-// valid UTF-8 after escape sequences are expanded.
+// Strings are written with " or ' and a (possibly empty) sequence of
+// intervening characters. Strings must be valid UTF-8 after expanding escape
+// sequences (described below).
//
// 'asdf'
// "that's cool"
@@ -55,6 +55,9 @@
// 'a multiline
// string'
//
+// Carriage returns (0x0d) are discarded from the string value. If you need a
+// string to contain carriage return, use the \r escape sequence.
+//
// Backslash characters inside a string are interpreted as an escape sequence.
// Any escape sequence not described below is an error. The escape sequences
// are identical to C11, with the exception that \x always takes exactly 2
@@ -162,6 +165,7 @@ import (
"reflect"
"strconv"
"strings"
+ "unicode"
"unicode/utf8"
)
@@ -357,8 +361,16 @@ func (p *parser) parseFloat(nBytes []byte) (float64, error) {
func (p *parser) unescape(rawStr []byte) ([]byte, error) {
var escaped []byte
for i := 0; i < len(rawStr); i++ {
+ if i+1 < len(rawStr) && rawStr[i] == '\r' && rawStr[i+1] == '\n' {
+ continue
+ }
if rawStr[i] != '\\' {
- escaped = append(escaped, rawStr[i])
+ r, n := utf8.DecodeRune(rawStr[i:])
+ if r != '\t' && r != '\n' && unicode.IsControl(r) {
+ return nil, p.error("control character %q must be escaped", r)
+ }
+ escaped = append(escaped, rawStr[i:i+n]...)
+ i += n - 1
continue
}
i++
diff --git a/ccl_test.go b/ccl_test.go
index e377306..e7405f8 100644
--- a/ccl_test.go
+++ b/ccl_test.go
@@ -260,6 +260,14 @@ can just span multiple lines"`,
msg: `string: '\033'`,
want: message{String: "\033"},
}, {
+ desc: "StringStripCarriageReturn",
+ msg: "string:'a\r\nb'",
+ want: message{String: "a\nb"},
+ }, {
+ desc: "StringTab",
+ msg: "string:'\t'",
+ want: message{String: "\t"},
+ }, {
desc: "Message",
msg: `message { field: 10 }`,
want: message{Message: &nestedMessage{Field: 10}},
@@ -408,6 +416,12 @@ func TestUnmarshal_Invalid(t *testing.T) {
desc: "StringBadUnicode",
msg: `string:"\ugggg"`,
}, {
+ desc: "StringControlCharacter",
+ msg: "string:'\a'",
+ }, {
+ desc: "StringCarriageReturnNotFollowedByNewline",
+ msg: "string:'\r'",
+ }, {
desc: "UnterminatedString",
msg: `string: '`,
}, {