diff options
Diffstat (limited to 'libgo/go/encoding/xml')
-rw-r--r-- | libgo/go/encoding/xml/marshal.go | 36 | ||||
-rw-r--r-- | libgo/go/encoding/xml/marshal_test.go | 51 | ||||
-rw-r--r-- | libgo/go/encoding/xml/read.go | 2 | ||||
-rw-r--r-- | libgo/go/encoding/xml/read_test.go | 21 | ||||
-rw-r--r-- | libgo/go/encoding/xml/typeinfo.go | 7 | ||||
-rw-r--r-- | libgo/go/encoding/xml/xml.go | 55 | ||||
-rw-r--r-- | libgo/go/encoding/xml/xml_test.go | 53 |
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) + } + } +} |