9fans.net/go@v0.0.7/plumb/plumb.go (about)

     1  // Package plumb provides routines for sending and receiving messages for the plumber.
     2  package plumb // import "9fans.net/go/plumb"
     3  
     4  import (
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  
    13  	"9fans.net/go/plan9/client"
    14  )
    15  
    16  // Message represents a message to or from the plumber.
    17  type Message struct {
    18  	Src  string     // The source of the message ("acme").
    19  	Dst  string     // The destination port of the message ("edit").
    20  	Dir  string     // The working directory in which to interpret the message.
    21  	Type string     // The type of the message ("text").
    22  	Attr *Attribute // The attributes; may be nil.
    23  	Data []byte     // The data; may be nil.
    24  }
    25  
    26  // Attribute represents a list of attributes for a single Message.
    27  type Attribute struct {
    28  	Name  string // The name of the attribute ("addr").
    29  	Value string // The value of the attribute ("/long johns/")
    30  	Next  *Attribute
    31  }
    32  
    33  var (
    34  	ErrAttribute = errors.New("bad attribute syntax")
    35  	ErrQuote     = errors.New("bad attribute quoting")
    36  )
    37  
    38  var fsys *client.Fsys
    39  var fsysErr error
    40  var fsysOnce sync.Once
    41  
    42  // Open opens the plumbing file with the given name and open mode.
    43  func Open(name string, mode int) (*client.Fid, error) {
    44  	fsysOnce.Do(mountPlumb)
    45  	if fsysErr != nil {
    46  		return nil, fsysErr
    47  	}
    48  	fid, err := fsys.Open(name, uint8(mode))
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	return fid, nil
    53  }
    54  
    55  // Send writes the message to the writer. The message will be sent with
    56  // a single call to Write.
    57  func (m *Message) Send(w io.Writer) error {
    58  	var buf bytes.Buffer
    59  	fmt.Fprintf(&buf, "%s\n", m.Src)
    60  	fmt.Fprintf(&buf, "%s\n", m.Dst)
    61  	fmt.Fprintf(&buf, "%s\n", m.Dir)
    62  	fmt.Fprintf(&buf, "%s\n", m.Type)
    63  	m.Attr.send(&buf)
    64  	fmt.Fprintf(&buf, "%d\n", len(m.Data))
    65  	buf.Write(m.Data)
    66  	_, err := w.Write(buf.Bytes())
    67  	return err
    68  }
    69  
    70  func (attr *Attribute) send(w io.Writer) {
    71  	for a := attr; a != nil; a = a.Next {
    72  		if a != attr {
    73  			fmt.Fprint(w, " ")
    74  		}
    75  		fmt.Fprintf(w, "%s=%s", a.Name, quoteAttribute(a.Value))
    76  	}
    77  	fmt.Fprintf(w, "\n")
    78  }
    79  
    80  const quote = '\''
    81  
    82  // quoteAttribute quotes the attribute value, if necessary, and returns the result.
    83  func quoteAttribute(s string) string {
    84  	if !strings.ContainsAny(s, " '=\t") {
    85  		return s
    86  	}
    87  	b := make([]byte, 0, 10+len(s)) // Room for a couple of quotes and a few backslashes.
    88  	b = append(b, quote)
    89  	for i := 0; i < len(s); i++ {
    90  		c := s[i]
    91  		if c == quote {
    92  			b = append(b, quote)
    93  		}
    94  		b = append(b, c)
    95  	}
    96  	b = append(b, quote)
    97  	return string(b)
    98  }
    99  
   100  // Recv reads a message from the reader and stores it in the Message.
   101  // Since encoded messages are properly delimited, Recv will not read
   102  // any data beyond the message itself.
   103  func (m *Message) Recv(r io.ByteReader) error {
   104  	reader := newReader(r)
   105  	m.Src = reader.readLine()
   106  	m.Dst = reader.readLine()
   107  	m.Dir = reader.readLine()
   108  	m.Type = reader.readLine()
   109  	m.Attr = reader.readAttr()
   110  	if reader.err != nil {
   111  		return reader.err
   112  	}
   113  	n, err := strconv.Atoi(reader.readLine())
   114  	if err != nil {
   115  		return err
   116  	}
   117  	m.Data = make([]byte, n)
   118  	reader.read(m.Data)
   119  	return reader.err
   120  }
   121  
   122  type reader struct {
   123  	r    io.ByteReader
   124  	buf  []byte
   125  	attr *Attribute
   126  	err  error
   127  }
   128  
   129  func newReader(r io.ByteReader) *reader {
   130  	return &reader{
   131  		r:   r,
   132  		buf: make([]byte, 128),
   133  	}
   134  }
   135  
   136  func (r *reader) readLine() string {
   137  	r.buf = r.buf[:0]
   138  	var c byte
   139  	for r.err == nil {
   140  		c, r.err = r.r.ReadByte()
   141  		if c == '\n' {
   142  			break
   143  		}
   144  		r.buf = append(r.buf, c)
   145  	}
   146  	return string(r.buf)
   147  }
   148  
   149  func (r *reader) read(p []byte) {
   150  	rr, ok := r.r.(io.Reader)
   151  	if r.err == nil && ok {
   152  		_, r.err = io.ReadFull(rr, p)
   153  		return
   154  	}
   155  	for i := range p {
   156  		if r.err != nil {
   157  			break
   158  		}
   159  		p[i], r.err = r.r.ReadByte()
   160  	}
   161  }
   162  
   163  func (r *reader) readAttr() *Attribute {
   164  	r.buf = r.buf[:0]
   165  	var c byte
   166  	quoting := false
   167  Loop:
   168  	for r.err == nil {
   169  		c, r.err = r.r.ReadByte()
   170  		if quoting && c == quote {
   171  			r.buf = append(r.buf, c)
   172  			c, r.err = r.r.ReadByte()
   173  			if c != quote {
   174  				quoting = false
   175  			}
   176  		}
   177  		if !quoting {
   178  			switch c {
   179  			case '\n':
   180  				break Loop
   181  			case quote:
   182  				quoting = true
   183  			case ' ':
   184  				r.newAttr()
   185  				r.buf = r.buf[:0]
   186  				continue Loop // Don't add the space.
   187  			}
   188  		}
   189  		r.buf = append(r.buf, c)
   190  	}
   191  	if len(r.buf) > 0 && r.err == nil {
   192  		r.newAttr()
   193  	}
   194  	// Attributes are ordered so reverse the list.
   195  	var next, rattr *Attribute
   196  	for a := r.attr; a != nil; a = next {
   197  		next = a.Next
   198  		a.Next = rattr
   199  		rattr = a
   200  	}
   201  	return rattr
   202  }
   203  
   204  func (r *reader) newAttr() {
   205  	equals := bytes.IndexByte(r.buf, '=')
   206  	if equals < 0 {
   207  		r.err = ErrAttribute
   208  		return
   209  	}
   210  	str := string(r.buf)
   211  	r.attr = &Attribute{
   212  		Name: str[:equals],
   213  		Next: r.attr,
   214  	}
   215  	r.attr.Value, r.err = unquoteAttribute(str[equals+1:])
   216  }
   217  
   218  // unquoteAttribute unquotes the attribute value, if necessary, and returns the result.
   219  func unquoteAttribute(s string) (string, error) {
   220  	if !strings.Contains(s, "'") {
   221  		return s, nil
   222  	}
   223  	if len(s) < 2 || s[0] != quote || s[len(s)-1] != quote {
   224  		return s, ErrQuote
   225  	}
   226  	s = s[1 : len(s)-1]
   227  	b := make([]byte, 0, len(s))
   228  	for i := 0; i < len(s); i++ {
   229  		c := s[i]
   230  		if c == quote { // Must be doubled.
   231  			if i == len(s)-1 || s[i+1] != quote {
   232  				return s, ErrQuote
   233  			}
   234  			i++
   235  		}
   236  		b = append(b, c)
   237  	}
   238  	return string(b), nil
   239  }
   240  
   241  // LookupAttr returns the value associated with the named attribute.
   242  // If the attribute is missing, LookupAttr returns an empty string.
   243  // To distinguish an empty present attribute from a missing attribute,
   244  // walk the m.Attr list directly instead of using LookupAttr.
   245  func (m *Message) LookupAttr(name string) string {
   246  	for a := m.Attr; a != nil; a = a.Next {
   247  		if a.Name == name {
   248  			return a.Value
   249  		}
   250  	}
   251  	return ""
   252  }