選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

tags.go 2.5KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // written by Daniel Oaks <daniel@danieloaks.net>
  2. // released under the ISC license
  3. package ircmsg
  4. import "strings"
  5. var (
  6. // valtoescape replaces real characters with message tag escapes.
  7. valtoescape = strings.NewReplacer("\\", "\\\\", ";", "\\:", " ", "\\s", "\r", "\\r", "\n", "\\n")
  8. escapedCharLookupTable [256]byte
  9. )
  10. func init() {
  11. // most chars escape to themselves
  12. for i := 0; i < 256; i += 1 {
  13. escapedCharLookupTable[i] = byte(i)
  14. }
  15. // these are the exceptions
  16. escapedCharLookupTable[':'] = ';'
  17. escapedCharLookupTable['s'] = ' '
  18. escapedCharLookupTable['r'] = '\r'
  19. escapedCharLookupTable['n'] = '\n'
  20. }
  21. // EscapeTagValue takes a value, and returns an escaped message tag value.
  22. //
  23. // This function is automatically used when lines are created from an
  24. // IRCMessage, so you don't need to call it yourself before creating a line.
  25. func EscapeTagValue(inString string) string {
  26. return valtoescape.Replace(inString)
  27. }
  28. // UnescapeTagValue takes an escaped message tag value, and returns the raw value.
  29. //
  30. // This function is automatically used when lines are interpreted by ParseLine,
  31. // so you don't need to call it yourself after parsing a line.
  32. func UnescapeTagValue(inString string) string {
  33. // buf.Len() == 0 is the fastpath where we have not needed to unescape any chars
  34. var buf strings.Builder
  35. remainder := inString
  36. for {
  37. backslashPos := strings.IndexByte(remainder, '\\')
  38. if backslashPos == -1 {
  39. if buf.Len() == 0 {
  40. return inString
  41. } else {
  42. buf.WriteString(remainder)
  43. break
  44. }
  45. } else if backslashPos == len(remainder)-1 {
  46. // trailing backslash, which we strip
  47. if buf.Len() == 0 {
  48. return inString[:len(inString)-1]
  49. } else {
  50. buf.WriteString(remainder[:len(remainder)-1])
  51. break
  52. }
  53. }
  54. // non-trailing backslash detected; we're now on the slowpath
  55. // where we modify the string
  56. if buf.Len() == 0 {
  57. buf.Grow(len(inString)) // just an optimization
  58. }
  59. buf.WriteString(remainder[:backslashPos])
  60. buf.WriteByte(escapedCharLookupTable[remainder[backslashPos+1]])
  61. remainder = remainder[backslashPos+2:]
  62. }
  63. return buf.String()
  64. }
  65. func validateTagName(name string) bool {
  66. if len(name) == 0 {
  67. return false
  68. }
  69. if name[0] == '+' {
  70. name = name[1:]
  71. }
  72. if len(name) == 0 {
  73. return false
  74. }
  75. // let's err on the side of leniency here; allow -./ (45-47) in any position
  76. for i := 0; i < len(name); i++ {
  77. c := name[i]
  78. if !(('-' <= c && c <= '/') || ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
  79. return false
  80. }
  81. }
  82. return true
  83. }