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