esc_tab = []byte("	")
esc_nl = []byte("
")
esc_cr = []byte("
")
+ esc_fffd = []byte("\uFFFD") // Unicode replacement character
)
// EscapeText writes to w the properly escaped XML equivalent
func EscapeText(w io.Writer, s []byte) error {
var esc []byte
last := 0
- for i, c := range s {
- switch c {
+ for i := 0; i < len(s); {
+ r, width := utf8.DecodeRune(s[i:])
+ i += width
+ switch r {
case '"':
esc = esc_quot
case '\'':
case '\r':
esc = esc_cr
default:
+ if !isInCharacterRange(r) {
+ esc = esc_fffd
+ break
+ }
continue
}
- if _, err := w.Write(s[last:i]); err != nil {
+ if _, err := w.Write(s[last : i-width]); err != nil {
return err
}
if _, err := w.Write(esc); err != nil {
return err
}
- last = i + 1
+ last = i
}
if _, err := w.Write(s[last:]); err != nil {
return err
package xml
import (
+ "bytes"
"fmt"
"io"
"reflect"
err := EscapeText(errWriter{}, []byte{'A'})
if err == nil || err.Error() != expectErr {
- t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
+ t.Errorf("have %v, want %v", err, expectErr)
+ }
+}
+
+func TestEscapeTextInvalidChar(t *testing.T) {
+ input := []byte("A \x00 terminated string.")
+ expected := "A \uFFFD terminated string."
+
+ buff := new(bytes.Buffer)
+ if err := EscapeText(buff, input); err != nil {
+ t.Fatalf("have %v, want nil", err)
+ }
+ text := buff.String()
+
+ if text != expected {
+ t.Errorf("have %v, want %v", text, expected)
}
}