aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/encoding/xml
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/encoding/xml')
-rw-r--r--libgo/go/encoding/xml/marshal.go36
-rw-r--r--libgo/go/encoding/xml/marshal_test.go51
-rw-r--r--libgo/go/encoding/xml/read.go2
-rw-r--r--libgo/go/encoding/xml/read_test.go21
-rw-r--r--libgo/go/encoding/xml/typeinfo.go7
-rw-r--r--libgo/go/encoding/xml/xml.go55
-rw-r--r--libgo/go/encoding/xml/xml_test.go53
7 files changed, 209 insertions, 16 deletions
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go
index 86d1422..8ebd693 100644
--- a/libgo/go/encoding/xml/marshal.go
+++ b/libgo/go/encoding/xml/marshal.go
@@ -48,6 +48,8 @@ const (
// field name in the XML element.
// - a field with tag ",chardata" is written as character data,
// not as an XML element.
+// - a field with tag ",cdata" is written as character data
+// wrapped in one or more <![CDATA[ ... ]]> tags, not as an XML element.
// - a field with tag ",innerxml" is written verbatim, not subject
// to the usual marshalling procedure.
// - a field with tag ",comment" is written as an XML comment, not
@@ -768,7 +770,11 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
}
switch finfo.flags & fMode {
- case fCharData:
+ case fCDATA, fCharData:
+ emit := EscapeText
+ if finfo.flags&fMode == fCDATA {
+ emit = emitCDATA
+ }
if err := s.trim(finfo.parents); err != nil {
return err
}
@@ -777,7 +783,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if err != nil {
return err
}
- Escape(p, data)
+ if err := emit(p, data); err != nil {
+ return err
+ }
continue
}
if vf.CanAddr() {
@@ -787,27 +795,37 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if err != nil {
return err
}
- Escape(p, data)
+ if err := emit(p, data); err != nil {
+ return err
+ }
continue
}
}
var scratch [64]byte
switch vf.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
+ if err := emit(p, strconv.AppendInt(scratch[:0], vf.Int(), 10)); err != nil {
+ return err
+ }
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
+ if err := emit(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10)); err != nil {
+ return err
+ }
case reflect.Float32, reflect.Float64:
- Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
+ if err := emit(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits())); err != nil {
+ return err
+ }
case reflect.Bool:
- Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
+ if err := emit(p, strconv.AppendBool(scratch[:0], vf.Bool())); err != nil {
+ return err
+ }
case reflect.String:
- if err := EscapeText(p, []byte(vf.String())); err != nil {
+ if err := emit(p, []byte(vf.String())); err != nil {
return err
}
case reflect.Slice:
if elem, ok := vf.Interface().([]byte); ok {
- if err := EscapeText(p, elem); err != nil {
+ if err := emit(p, elem); err != nil {
return err
}
}
diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go
index ef6c20e..fe8b16f 100644
--- a/libgo/go/encoding/xml/marshal_test.go
+++ b/libgo/go/encoding/xml/marshal_test.go
@@ -356,6 +356,15 @@ type NestedAndComment struct {
Comment string `xml:",comment"`
}
+type CDataTest struct {
+ Chardata string `xml:",cdata"`
+}
+
+type NestedAndCData struct {
+ AB []string `xml:"A>B"`
+ CDATA string `xml:",cdata"`
+}
+
func ifaceptr(x interface{}) interface{} {
return &x
}
@@ -978,6 +987,42 @@ var marshalTests = []struct {
MyInt: 42,
},
},
+ // Test outputting CDATA-wrapped text.
+ {
+ ExpectXML: `<CDataTest></CDataTest>`,
+ Value: &CDataTest{},
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[http://example.com/tests/1?foo=1&bar=baz]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "http://example.com/tests/1?foo=1&bar=baz",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[Literal <![CDATA[Nested]]]]><![CDATA[>!]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "Literal <![CDATA[Nested]]>!",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "<![CDATA[Nested]]> Literal!",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal! <![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "<![CDATA[Nested]]> Literal! <![CDATA[Nested]]> Literal!",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[<![CDATA[<![CDATA[Nested]]]]><![CDATA[>]]]]><![CDATA[>]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "<![CDATA[<![CDATA[Nested]]>]]>",
+ },
+ },
+
// Test omitempty with parent chain; see golang.org/issue/4168.
{
ExpectXML: `<Strings><A></A></Strings>`,
@@ -1016,6 +1061,10 @@ var marshalTests = []struct {
ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"},
},
+ {
+ ExpectXML: `<NestedAndCData><A><B></B><B></B></A><![CDATA[test]]></NestedAndCData>`,
+ Value: &NestedAndCData{AB: make([]string, 2), CDATA: "test"},
+ },
}
func TestMarshal(t *testing.T) {
@@ -1705,7 +1754,7 @@ func TestRace9796(t *testing.T) {
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
- Marshal(B{[]A{A{}}})
+ Marshal(B{[]A{{}}})
wg.Done()
}()
}
diff --git a/libgo/go/encoding/xml/read.go b/libgo/go/encoding/xml/read.go
index 75b9f2b..77b4c7b 100644
--- a/libgo/go/encoding/xml/read.go
+++ b/libgo/go/encoding/xml/read.go
@@ -431,7 +431,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
}
- case fCharData:
+ case fCDATA, fCharData:
if !saveData.IsValid() {
saveData = finfo.value(sv)
}
diff --git a/libgo/go/encoding/xml/read_test.go b/libgo/go/encoding/xml/read_test.go
index 7d004dc..7a98092 100644
--- a/libgo/go/encoding/xml/read_test.go
+++ b/libgo/go/encoding/xml/read_test.go
@@ -712,3 +712,24 @@ func TestUnmarshalIntoInterface(t *testing.T) {
t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)
}
}
+
+type X struct {
+ D string `xml:",comment"`
+}
+
+// Issue 11112. Unmarshal must reject invalid comments.
+func TestMalformedComment(t *testing.T) {
+ testData := []string{
+ "<X><!-- a---></X>",
+ "<X><!-- -- --></X>",
+ "<X><!-- a--b --></X>",
+ "<X><!------></X>",
+ }
+ for i, test := range testData {
+ data := []byte(test)
+ v := new(X)
+ if err := Unmarshal(data, v); err == nil {
+ t.Errorf("%d: unmarshal should reject invalid comments", i)
+ }
+ }
+}
diff --git a/libgo/go/encoding/xml/typeinfo.go b/libgo/go/encoding/xml/typeinfo.go
index 6766b88..6483c8d 100644
--- a/libgo/go/encoding/xml/typeinfo.go
+++ b/libgo/go/encoding/xml/typeinfo.go
@@ -31,6 +31,7 @@ type fieldFlags int
const (
fElement fieldFlags = 1 << iota
fAttr
+ fCDATA
fCharData
fInnerXml
fComment
@@ -38,7 +39,7 @@ const (
fOmitEmpty
- fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
+ fMode = fElement | fAttr | fCDATA | fCharData | fInnerXml | fComment | fAny
)
var tinfoMap = make(map[reflect.Type]*typeInfo)
@@ -130,6 +131,8 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
switch flag {
case "attr":
finfo.flags |= fAttr
+ case "cdata":
+ finfo.flags |= fCDATA
case "chardata":
finfo.flags |= fCharData
case "innerxml":
@@ -148,7 +151,7 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
switch mode := finfo.flags & fMode; mode {
case 0:
finfo.flags |= fElement
- case fAttr, fCharData, fInnerXml, fComment, fAny:
+ case fAttr, fCDATA, fCharData, fInnerXml, fComment, fAny:
if f.Name == "XMLName" || tag != "" && mode != fAttr {
valid = false
}
diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go
index 0a21c93..45f4157 100644
--- a/libgo/go/encoding/xml/xml.go
+++ b/libgo/go/encoding/xml/xml.go
@@ -227,7 +227,8 @@ func NewDecoder(r io.Reader) *Decoder {
//
// Token guarantees that the StartElement and EndElement
// tokens it returns are properly nested and matched:
-// if Token encounters an unexpected end element,
+// if Token encounters an unexpected end element
+// or EOF before all expected end elements,
// it will return an error.
//
// Token implements XML name spaces as described by
@@ -245,6 +246,9 @@ func (d *Decoder) Token() (t Token, err error) {
t = d.nextToken
d.nextToken = nil
} else if t, err = d.rawToken(); err != nil {
+ if err == io.EOF && d.stk != nil && d.stk.kind != stkEOF {
+ err = d.syntaxError("unexpected EOF")
+ }
return
}
@@ -580,7 +584,7 @@ func (d *Decoder) rawToken() (Token, error) {
return nil, d.err
}
enc := procInst("encoding", content)
- if enc != "" && enc != "utf-8" && enc != "UTF-8" {
+ if enc != "" && enc != "utf-8" && enc != "UTF-8" && !strings.EqualFold(enc, "utf-8") {
if d.CharsetReader == nil {
d.err = fmt.Errorf("xml: encoding %q declared but Decoder.CharsetReader is nil", enc)
return nil, d.err
@@ -621,7 +625,12 @@ func (d *Decoder) rawToken() (Token, error) {
return nil, d.err
}
d.buf.WriteByte(b)
- if b0 == '-' && b1 == '-' && b == '>' {
+ if b0 == '-' && b1 == '-' {
+ if b != '>' {
+ d.err = d.syntaxError(
+ `invalid sequence "--" not allowed in comments`)
+ return nil, d.err
+ }
break
}
b0, b1 = b1, b
@@ -1940,6 +1949,46 @@ func Escape(w io.Writer, s []byte) {
EscapeText(w, s)
}
+var (
+ cdataStart = []byte("<![CDATA[")
+ cdataEnd = []byte("]]>")
+ cdataEscape = []byte("]]]]><![CDATA[>")
+)
+
+// emitCDATA writes to w the CDATA-wrapped plain text data s.
+// It escapes CDATA directives nested in s.
+func emitCDATA(w io.Writer, s []byte) error {
+ if len(s) == 0 {
+ return nil
+ }
+ if _, err := w.Write(cdataStart); err != nil {
+ return err
+ }
+ for {
+ i := bytes.Index(s, cdataEnd)
+ if i >= 0 && i+len(cdataEnd) <= len(s) {
+ // Found a nested CDATA directive end.
+ if _, err := w.Write(s[:i]); err != nil {
+ return err
+ }
+ if _, err := w.Write(cdataEscape); err != nil {
+ return err
+ }
+ i += len(cdataEnd)
+ } else {
+ if _, err := w.Write(s); err != nil {
+ return err
+ }
+ break
+ }
+ s = s[i:]
+ }
+ if _, err := w.Write(cdataEnd); err != nil {
+ return err
+ }
+ return nil
+}
+
// procInst parses the `param="..."` or `param='...'`
// value out of the provided string, returning "" if not found.
func procInst(param, s string) string {
diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go
index 312a7c9..5d5e4bf 100644
--- a/libgo/go/encoding/xml/xml_test.go
+++ b/libgo/go/encoding/xml/xml_test.go
@@ -750,3 +750,56 @@ func TestIssue5880(t *testing.T) {
t.Errorf("Marshal generated invalid UTF-8: %x", data)
}
}
+
+func TestIssue11405(t *testing.T) {
+ testCases := []string{
+ "<root>",
+ "<root><foo>",
+ "<root><foo></foo>",
+ }
+ for _, tc := range testCases {
+ d := NewDecoder(strings.NewReader(tc))
+ var err error
+ for {
+ _, err = d.Token()
+ if err != nil {
+ break
+ }
+ }
+ if _, ok := err.(*SyntaxError); !ok {
+ t.Errorf("%s: Token: Got error %v, want SyntaxError", tc, err)
+ }
+ }
+}
+
+func TestIssue12417(t *testing.T) {
+ testCases := []struct {
+ s string
+ ok bool
+ }{
+ {`<?xml encoding="UtF-8" version="1.0"?><root/>`, true},
+ {`<?xml encoding="UTF-8" version="1.0"?><root/>`, true},
+ {`<?xml encoding="utf-8" version="1.0"?><root/>`, true},
+ {`<?xml encoding="uuu-9" version="1.0"?><root/>`, false},
+ }
+ for _, tc := range testCases {
+ d := NewDecoder(strings.NewReader(tc.s))
+ var err error
+ for {
+ _, err = d.Token()
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ break
+ }
+ }
+ if err != nil && tc.ok {
+ t.Errorf("%q: Encoding charset: expected no error, got %s", tc.s, err)
+ continue
+ }
+ if err == nil && !tc.ok {
+ t.Errorf("%q: Encoding charset: expected error, got nil", tc.s)
+ }
+ }
+}