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