You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
4.5 KiB

  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package dict implements the Dictionary Server Protocol
  5. // as defined in RFC 2229.
  6. package dict // import "golang.org/x/net/dict"
  7. import (
  8. "net/textproto"
  9. "strconv"
  10. "strings"
  11. )
  12. // A Client represents a client connection to a dictionary server.
  13. type Client struct {
  14. text *textproto.Conn
  15. }
  16. // Dial returns a new client connected to a dictionary server at
  17. // addr on the given network.
  18. func Dial(network, addr string) (*Client, error) {
  19. text, err := textproto.Dial(network, addr)
  20. if err != nil {
  21. return nil, err
  22. }
  23. _, _, err = text.ReadCodeLine(220)
  24. if err != nil {
  25. text.Close()
  26. return nil, err
  27. }
  28. return &Client{text: text}, nil
  29. }
  30. // Close closes the connection to the dictionary server.
  31. func (c *Client) Close() error {
  32. return c.text.Close()
  33. }
  34. // A Dict represents a dictionary available on the server.
  35. type Dict struct {
  36. Name string // short name of dictionary
  37. Desc string // long description
  38. }
  39. // Dicts returns a list of the dictionaries available on the server.
  40. func (c *Client) Dicts() ([]Dict, error) {
  41. id, err := c.text.Cmd("SHOW DB")
  42. if err != nil {
  43. return nil, err
  44. }
  45. c.text.StartResponse(id)
  46. defer c.text.EndResponse(id)
  47. _, _, err = c.text.ReadCodeLine(110)
  48. if err != nil {
  49. return nil, err
  50. }
  51. lines, err := c.text.ReadDotLines()
  52. if err != nil {
  53. return nil, err
  54. }
  55. _, _, err = c.text.ReadCodeLine(250)
  56. dicts := make([]Dict, len(lines))
  57. for i := range dicts {
  58. d := &dicts[i]
  59. a, _ := fields(lines[i])
  60. if len(a) < 2 {
  61. return nil, textproto.ProtocolError("invalid dictionary: " + lines[i])
  62. }
  63. d.Name = a[0]
  64. d.Desc = a[1]
  65. }
  66. return dicts, err
  67. }
  68. // A Defn represents a definition.
  69. type Defn struct {
  70. Dict Dict // Dict where definition was found
  71. Word string // Word being defined
  72. Text []byte // Definition text, typically multiple lines
  73. }
  74. // Define requests the definition of the given word.
  75. // The argument dict names the dictionary to use,
  76. // the Name field of a Dict returned by Dicts.
  77. //
  78. // The special dictionary name "*" means to look in all the
  79. // server's dictionaries.
  80. // The special dictionary name "!" means to look in all the
  81. // server's dictionaries in turn, stopping after finding the word
  82. // in one of them.
  83. func (c *Client) Define(dict, word string) ([]*Defn, error) {
  84. id, err := c.text.Cmd("DEFINE %s %q", dict, word)
  85. if err != nil {
  86. return nil, err
  87. }
  88. c.text.StartResponse(id)
  89. defer c.text.EndResponse(id)
  90. _, line, err := c.text.ReadCodeLine(150)
  91. if err != nil {
  92. return nil, err
  93. }
  94. a, _ := fields(line)
  95. if len(a) < 1 {
  96. return nil, textproto.ProtocolError("malformed response: " + line)
  97. }
  98. n, err := strconv.Atoi(a[0])
  99. if err != nil {
  100. return nil, textproto.ProtocolError("invalid definition count: " + a[0])
  101. }
  102. def := make([]*Defn, n)
  103. for i := 0; i < n; i++ {
  104. _, line, err = c.text.ReadCodeLine(151)
  105. if err != nil {
  106. return nil, err
  107. }
  108. a, _ := fields(line)
  109. if len(a) < 3 {
  110. // skip it, to keep protocol in sync
  111. i--
  112. n--
  113. def = def[0:n]
  114. continue
  115. }
  116. d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}}
  117. d.Text, err = c.text.ReadDotBytes()
  118. if err != nil {
  119. return nil, err
  120. }
  121. def[i] = d
  122. }
  123. _, _, err = c.text.ReadCodeLine(250)
  124. return def, err
  125. }
  126. // Fields returns the fields in s.
  127. // Fields are space separated unquoted words
  128. // or quoted with single or double quote.
  129. func fields(s string) ([]string, error) {
  130. var v []string
  131. i := 0
  132. for {
  133. for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
  134. i++
  135. }
  136. if i >= len(s) {
  137. break
  138. }
  139. if s[i] == '"' || s[i] == '\'' {
  140. q := s[i]
  141. // quoted string
  142. var j int
  143. for j = i + 1; ; j++ {
  144. if j >= len(s) {
  145. return nil, textproto.ProtocolError("malformed quoted string")
  146. }
  147. if s[j] == '\\' {
  148. j++
  149. continue
  150. }
  151. if s[j] == q {
  152. j++
  153. break
  154. }
  155. }
  156. v = append(v, unquote(s[i+1:j-1]))
  157. i = j
  158. } else {
  159. // atom
  160. var j int
  161. for j = i; j < len(s); j++ {
  162. if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' {
  163. break
  164. }
  165. }
  166. v = append(v, s[i:j])
  167. i = j
  168. }
  169. if i < len(s) {
  170. c := s[i]
  171. if c != ' ' && c != '\t' {
  172. return nil, textproto.ProtocolError("quotes not on word boundaries")
  173. }
  174. }
  175. }
  176. return v, nil
  177. }
  178. func unquote(s string) string {
  179. if strings.Index(s, "\\") < 0 {
  180. return s
  181. }
  182. b := []byte(s)
  183. w := 0
  184. for r := 0; r < len(b); r++ {
  185. c := b[r]
  186. if c == '\\' {
  187. r++
  188. c = b[r]
  189. }
  190. b[w] = c
  191. w++
  192. }
  193. return string(b[0:w])
  194. }