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  }