211 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2010 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
// Package dict implements the Dictionary Server Protocol
 | 
						|
// as defined in RFC 2229.
 | 
						|
package dict // import "golang.org/x/net/dict"
 | 
						|
 | 
						|
import (
 | 
						|
	"net/textproto"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// A Client represents a client connection to a dictionary server.
 | 
						|
type Client struct {
 | 
						|
	text *textproto.Conn
 | 
						|
}
 | 
						|
 | 
						|
// Dial returns a new client connected to a dictionary server at
 | 
						|
// addr on the given network.
 | 
						|
func Dial(network, addr string) (*Client, error) {
 | 
						|
	text, err := textproto.Dial(network, addr)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	_, _, err = text.ReadCodeLine(220)
 | 
						|
	if err != nil {
 | 
						|
		text.Close()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &Client{text: text}, nil
 | 
						|
}
 | 
						|
 | 
						|
// Close closes the connection to the dictionary server.
 | 
						|
func (c *Client) Close() error {
 | 
						|
	return c.text.Close()
 | 
						|
}
 | 
						|
 | 
						|
// A Dict represents a dictionary available on the server.
 | 
						|
type Dict struct {
 | 
						|
	Name string // short name of dictionary
 | 
						|
	Desc string // long description
 | 
						|
}
 | 
						|
 | 
						|
// Dicts returns a list of the dictionaries available on the server.
 | 
						|
func (c *Client) Dicts() ([]Dict, error) {
 | 
						|
	id, err := c.text.Cmd("SHOW DB")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	c.text.StartResponse(id)
 | 
						|
	defer c.text.EndResponse(id)
 | 
						|
 | 
						|
	_, _, err = c.text.ReadCodeLine(110)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	lines, err := c.text.ReadDotLines()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	_, _, err = c.text.ReadCodeLine(250)
 | 
						|
 | 
						|
	dicts := make([]Dict, len(lines))
 | 
						|
	for i := range dicts {
 | 
						|
		d := &dicts[i]
 | 
						|
		a, _ := fields(lines[i])
 | 
						|
		if len(a) < 2 {
 | 
						|
			return nil, textproto.ProtocolError("invalid dictionary: " + lines[i])
 | 
						|
		}
 | 
						|
		d.Name = a[0]
 | 
						|
		d.Desc = a[1]
 | 
						|
	}
 | 
						|
	return dicts, err
 | 
						|
}
 | 
						|
 | 
						|
// A Defn represents a definition.
 | 
						|
type Defn struct {
 | 
						|
	Dict Dict   // Dict where definition was found
 | 
						|
	Word string // Word being defined
 | 
						|
	Text []byte // Definition text, typically multiple lines
 | 
						|
}
 | 
						|
 | 
						|
// Define requests the definition of the given word.
 | 
						|
// The argument dict names the dictionary to use,
 | 
						|
// the Name field of a Dict returned by Dicts.
 | 
						|
//
 | 
						|
// The special dictionary name "*" means to look in all the
 | 
						|
// server's dictionaries.
 | 
						|
// The special dictionary name "!" means to look in all the
 | 
						|
// server's dictionaries in turn, stopping after finding the word
 | 
						|
// in one of them.
 | 
						|
func (c *Client) Define(dict, word string) ([]*Defn, error) {
 | 
						|
	id, err := c.text.Cmd("DEFINE %s %q", dict, word)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	c.text.StartResponse(id)
 | 
						|
	defer c.text.EndResponse(id)
 | 
						|
 | 
						|
	_, line, err := c.text.ReadCodeLine(150)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	a, _ := fields(line)
 | 
						|
	if len(a) < 1 {
 | 
						|
		return nil, textproto.ProtocolError("malformed response: " + line)
 | 
						|
	}
 | 
						|
	n, err := strconv.Atoi(a[0])
 | 
						|
	if err != nil {
 | 
						|
		return nil, textproto.ProtocolError("invalid definition count: " + a[0])
 | 
						|
	}
 | 
						|
	def := make([]*Defn, n)
 | 
						|
	for i := 0; i < n; i++ {
 | 
						|
		_, line, err = c.text.ReadCodeLine(151)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		a, _ := fields(line)
 | 
						|
		if len(a) < 3 {
 | 
						|
			// skip it, to keep protocol in sync
 | 
						|
			i--
 | 
						|
			n--
 | 
						|
			def = def[0:n]
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}}
 | 
						|
		d.Text, err = c.text.ReadDotBytes()
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		def[i] = d
 | 
						|
	}
 | 
						|
	_, _, err = c.text.ReadCodeLine(250)
 | 
						|
	return def, err
 | 
						|
}
 | 
						|
 | 
						|
// Fields returns the fields in s.
 | 
						|
// Fields are space separated unquoted words
 | 
						|
// or quoted with single or double quote.
 | 
						|
func fields(s string) ([]string, error) {
 | 
						|
	var v []string
 | 
						|
	i := 0
 | 
						|
	for {
 | 
						|
		for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
 | 
						|
			i++
 | 
						|
		}
 | 
						|
		if i >= len(s) {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		if s[i] == '"' || s[i] == '\'' {
 | 
						|
			q := s[i]
 | 
						|
			// quoted string
 | 
						|
			var j int
 | 
						|
			for j = i + 1; ; j++ {
 | 
						|
				if j >= len(s) {
 | 
						|
					return nil, textproto.ProtocolError("malformed quoted string")
 | 
						|
				}
 | 
						|
				if s[j] == '\\' {
 | 
						|
					j++
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if s[j] == q {
 | 
						|
					j++
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			v = append(v, unquote(s[i+1:j-1]))
 | 
						|
			i = j
 | 
						|
		} else {
 | 
						|
			// atom
 | 
						|
			var j int
 | 
						|
			for j = i; j < len(s); j++ {
 | 
						|
				if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' {
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			v = append(v, s[i:j])
 | 
						|
			i = j
 | 
						|
		}
 | 
						|
		if i < len(s) {
 | 
						|
			c := s[i]
 | 
						|
			if c != ' ' && c != '\t' {
 | 
						|
				return nil, textproto.ProtocolError("quotes not on word boundaries")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return v, nil
 | 
						|
}
 | 
						|
 | 
						|
func unquote(s string) string {
 | 
						|
	if strings.Index(s, "\\") < 0 {
 | 
						|
		return s
 | 
						|
	}
 | 
						|
	b := []byte(s)
 | 
						|
	w := 0
 | 
						|
	for r := 0; r < len(b); r++ {
 | 
						|
		c := b[r]
 | 
						|
		if c == '\\' {
 | 
						|
			r++
 | 
						|
			c = b[r]
 | 
						|
		}
 | 
						|
		b[w] = c
 | 
						|
		w++
 | 
						|
	}
 | 
						|
	return string(b[0:w])
 | 
						|
}
 |