util.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package xml // import "github.com/tdewolff/parse/xml"
  2. import "github.com/tdewolff/parse"
  3. var (
  4. ltEntityBytes = []byte("<")
  5. ampEntityBytes = []byte("&")
  6. singleQuoteEntityBytes = []byte("'")
  7. doubleQuoteEntityBytes = []byte(""")
  8. )
  9. // EscapeAttrVal returns the escape attribute value bytes without quotes.
  10. func EscapeAttrVal(buf *[]byte, b []byte) []byte {
  11. singles := 0
  12. doubles := 0
  13. for i, c := range b {
  14. if c == '&' {
  15. if quote, n := parse.QuoteEntity(b[i:]); n > 0 {
  16. if quote == '"' {
  17. doubles++
  18. } else {
  19. singles++
  20. }
  21. }
  22. } else if c == '"' {
  23. doubles++
  24. } else if c == '\'' {
  25. singles++
  26. }
  27. }
  28. n := len(b) + 2
  29. var quote byte
  30. var escapedQuote []byte
  31. if doubles > singles {
  32. n += singles * 4
  33. quote = '\''
  34. escapedQuote = singleQuoteEntityBytes
  35. } else {
  36. n += doubles * 4
  37. quote = '"'
  38. escapedQuote = doubleQuoteEntityBytes
  39. }
  40. if n > cap(*buf) {
  41. *buf = make([]byte, 0, n) // maximum size, not actual size
  42. }
  43. t := (*buf)[:n] // maximum size, not actual size
  44. t[0] = quote
  45. j := 1
  46. start := 0
  47. for i, c := range b {
  48. if c == '&' {
  49. if entityQuote, n := parse.QuoteEntity(b[i:]); n > 0 {
  50. j += copy(t[j:], b[start:i])
  51. if entityQuote != quote {
  52. t[j] = entityQuote
  53. j++
  54. } else {
  55. j += copy(t[j:], escapedQuote)
  56. }
  57. start = i + n
  58. }
  59. } else if c == quote {
  60. j += copy(t[j:], b[start:i])
  61. j += copy(t[j:], escapedQuote)
  62. start = i + 1
  63. }
  64. }
  65. j += copy(t[j:], b[start:])
  66. t[j] = quote
  67. return t[:j+1]
  68. }
  69. // EscapeCDATAVal returns the escaped text bytes.
  70. func EscapeCDATAVal(buf *[]byte, b []byte) ([]byte, bool) {
  71. n := 0
  72. for _, c := range b {
  73. if c == '<' || c == '&' {
  74. if c == '<' {
  75. n += 3 // &lt;
  76. } else {
  77. n += 4 // &amp;
  78. }
  79. if n > len("<![CDATA[]]>") {
  80. return b, false
  81. }
  82. }
  83. }
  84. if len(b)+n > cap(*buf) {
  85. *buf = make([]byte, 0, len(b)+n)
  86. }
  87. t := (*buf)[:len(b)+n]
  88. j := 0
  89. start := 0
  90. for i, c := range b {
  91. if c == '<' {
  92. j += copy(t[j:], b[start:i])
  93. j += copy(t[j:], ltEntityBytes)
  94. start = i + 1
  95. } else if c == '&' {
  96. j += copy(t[j:], b[start:i])
  97. j += copy(t[j:], ampEntityBytes)
  98. start = i + 1
  99. }
  100. }
  101. j += copy(t[j:], b[start:])
  102. return t[:j], true
  103. }