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 }