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  }