util.go 3.7 KB

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