| | |
| | |
| | |
| |
|
| | |
| | package html |
| |
|
| | import ( |
| | "strings" |
| | "unicode/utf8" |
| | ) |
| |
|
| | |
| | |
| | |
| | var replacementTable = [...]rune{ |
| | '\u20AC', |
| | '\u0081', |
| | '\u201A', |
| | '\u0192', |
| | '\u201E', |
| | '\u2026', |
| | '\u2020', |
| | '\u2021', |
| | '\u02C6', |
| | '\u2030', |
| | '\u0160', |
| | '\u2039', |
| | '\u0152', |
| | '\u008D', |
| | '\u017D', |
| | '\u008F', |
| | '\u0090', |
| | '\u2018', |
| | '\u2019', |
| | '\u201C', |
| | '\u201D', |
| | '\u2022', |
| | '\u2013', |
| | '\u2014', |
| | '\u02DC', |
| | '\u2122', |
| | '\u0161', |
| | '\u203A', |
| | '\u0153', |
| | '\u009D', |
| | '\u017E', |
| | '\u0178', |
| | |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | func unescapeEntity(b []byte, dst, src int, entity map[string]rune, entity2 map[string][2]rune) (dst1, src1 int) { |
| | const attribute = false |
| |
|
| | |
| |
|
| | |
| | i, s := 1, b[src:] |
| |
|
| | if len(s) <= 1 { |
| | b[dst] = b[src] |
| | return dst + 1, src + 1 |
| | } |
| |
|
| | if s[i] == '#' { |
| | if len(s) <= 3 { |
| | b[dst] = b[src] |
| | return dst + 1, src + 1 |
| | } |
| | i++ |
| | c := s[i] |
| | hex := false |
| | if c == 'x' || c == 'X' { |
| | hex = true |
| | i++ |
| | } |
| |
|
| | x := '\x00' |
| | for i < len(s) { |
| | c = s[i] |
| | i++ |
| | if hex { |
| | if '0' <= c && c <= '9' { |
| | x = 16*x + rune(c) - '0' |
| | continue |
| | } else if 'a' <= c && c <= 'f' { |
| | x = 16*x + rune(c) - 'a' + 10 |
| | continue |
| | } else if 'A' <= c && c <= 'F' { |
| | x = 16*x + rune(c) - 'A' + 10 |
| | continue |
| | } |
| | } else if '0' <= c && c <= '9' { |
| | x = 10*x + rune(c) - '0' |
| | continue |
| | } |
| | if c != ';' { |
| | i-- |
| | } |
| | break |
| | } |
| |
|
| | if i <= 3 { |
| | b[dst] = b[src] |
| | return dst + 1, src + 1 |
| | } |
| |
|
| | if 0x80 <= x && x <= 0x9F { |
| | |
| | x = replacementTable[x-0x80] |
| | } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { |
| | |
| | x = '\uFFFD' |
| | } |
| |
|
| | return dst + utf8.EncodeRune(b[dst:], x), src + i |
| | } |
| |
|
| | |
| | |
| |
|
| | for i < len(s) { |
| | c := s[i] |
| | i++ |
| | |
| | if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { |
| | continue |
| | } |
| | if c != ';' { |
| | i-- |
| | } |
| | break |
| | } |
| |
|
| | entityName := s[1:i] |
| | if len(entityName) == 0 { |
| | |
| | } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { |
| | |
| | } else if x := entity[string(entityName)]; x != 0 { |
| | return dst + utf8.EncodeRune(b[dst:], x), src + i |
| | } else if x := entity2[string(entityName)]; x[0] != 0 { |
| | dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) |
| | return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i |
| | } else if !attribute { |
| | maxLen := len(entityName) - 1 |
| | if maxLen > longestEntityWithoutSemicolon { |
| | maxLen = longestEntityWithoutSemicolon |
| | } |
| | for j := maxLen; j > 1; j-- { |
| | if x := entity[string(entityName[:j])]; x != 0 { |
| | return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 |
| | } |
| | } |
| | } |
| |
|
| | dst1, src1 = dst+i, src+i |
| | copy(b[dst:dst1], b[src:src1]) |
| | return dst1, src1 |
| | } |
| |
|
| | var htmlEscaper = strings.NewReplacer( |
| | `&`, "&", |
| | `'`, "'", |
| | `<`, "<", |
| | `>`, ">", |
| | `"`, """, |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | func EscapeString(s string) string { |
| | return htmlEscaper.Replace(s) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func UnescapeString(s string) string { |
| | i := strings.IndexByte(s, '&') |
| |
|
| | if i < 0 { |
| | return s |
| | } |
| |
|
| | b := []byte(s) |
| | entity, entity2 := entityMaps() |
| | dst, src := unescapeEntity(b, i, i, entity, entity2) |
| | for len(s[src:]) > 0 { |
| | if s[src] == '&' { |
| | i = 0 |
| | } else { |
| | i = strings.IndexByte(s[src:], '&') |
| | } |
| | if i < 0 { |
| | dst += copy(b[dst:], s[src:]) |
| | break |
| | } |
| |
|
| | if i > 0 { |
| | copy(b[dst:], s[src:src+i]) |
| | } |
| | dst, src = unescapeEntity(b, dst+i, src+i, entity, entity2) |
| | } |
| | return string(b[:dst]) |
| | } |
| |
|