** * Replace entities like " ", "{", and "�" with the characters they encode. * See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references */
(text string)
| 862 | * See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references |
| 863 | */ |
| 864 | func decodeEntities(text string) string { |
| 865 | i := strings.IndexByte(text, '&') |
| 866 | if i < 0 { |
| 867 | return text |
| 868 | } |
| 869 | |
| 870 | var result strings.Builder |
| 871 | result.Grow(len(text)) |
| 872 | for { |
| 873 | result.WriteString(text[:i]) |
| 874 | text = text[i:] |
| 875 | |
| 876 | semi := strings.IndexByte(text, ';') |
| 877 | if semi < 0 { |
| 878 | break |
| 879 | } |
| 880 | |
| 881 | // Skip past any intervening '&' characters between the current '&' |
| 882 | // and the ';'. Each such '&' is not part of a valid entity, so emit |
| 883 | // it (and any text before the next '&') as literals. |
| 884 | for { |
| 885 | nextAmp := strings.IndexByte(text[1:semi], '&') |
| 886 | if nextAmp < 0 { |
| 887 | break |
| 888 | } |
| 889 | result.WriteString(text[:nextAmp+1]) |
| 890 | text = text[nextAmp+1:] |
| 891 | semi -= nextAmp + 1 |
| 892 | } |
| 893 | |
| 894 | entity := text[1:semi] |
| 895 | decoded, ok := decodeEntity(entity) |
| 896 | if ok { |
| 897 | // Use the JS-string encoder so lone surrogates (e.g. "�") |
| 898 | // are preserved rather than being lost to U+FFFD by WriteRune. |
| 899 | result.WriteString(stringutil.EncodeJSStringRune(decoded)) |
| 900 | } else { |
| 901 | result.WriteString(text[:semi+1]) |
| 902 | } |
| 903 | text = text[semi+1:] |
| 904 | |
| 905 | i = strings.IndexByte(text, '&') |
| 906 | if i < 0 { |
| 907 | break |
| 908 | } |
| 909 | } |
| 910 | result.WriteString(text) |
| 911 | return result.String() |
| 912 | } |
| 913 | |
| 914 | func decodeEntity(entity string) (rune, bool) { |
| 915 | if len(entity) == 0 { |
no test coverage detected