11package plan9
22
33import (
4+ "bytes"
45 "fmt"
56 "strconv"
7+ "strings"
68)
79
810type ProtocolError string
@@ -43,6 +45,10 @@ var nullDir = Dir{
4345 "" ,
4446}
4547
48+ func (d * Dir ) IsNull () bool {
49+ return * d == nullDir
50+ }
51+
4652func (d * Dir ) Null () {
4753 * d = nullDir
4854}
@@ -102,12 +108,96 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
102108}
103109
104110func (d * Dir ) String () string {
105- return fmt .Sprintf ("'%s' '%s' '%s' '%s' q %v m %#o at %d mt %d l %d t %d d %d" ,
111+ return fmt .Sprintf ("name '%s' uid '%s' gid '%s' muid '%s' qid %v mode %v atime %d mtime %d length %d type %d dev %d" ,
106112 d .Name , d .Uid , d .Gid , d .Muid , d .Qid , d .Mode ,
107113 d .Atime , d .Mtime , d .Length , d .Type , d .Dev )
108114}
109115
116+ func parseDir (s string ) (* Dir , error ) {
117+ d := new (Dir )
118+ d .Null ()
119+ for s != "" {
120+ s = strings .TrimSpace (s )
121+ if s == "" {
122+ break
123+ }
124+ var name string
125+ name , s , _ = strings .Cut (s , " " )
126+ s = strings .TrimSpace (s )
127+ var arg string
128+ if strings .HasPrefix (s , "'" ) {
129+ i := strings .Index (s [1 :], "'" )
130+ if i < 0 {
131+ return nil , fmt .Errorf ("missing closing quote" )
132+ }
133+ arg , s = s [1 :1 + i ], s [1 + i + 1 :]
134+ } else {
135+ arg , s , _ = strings .Cut (s , " " )
136+ }
137+ switch name {
138+ default :
139+ return nil , fmt .Errorf ("unknown field %q" , name )
140+ case "type" :
141+ n , err := strconv .ParseUint (arg , 0 , 16 )
142+ if err != nil {
143+ return nil , fmt .Errorf ("invalid type: %v" , err )
144+ }
145+ d .Type = uint16 (n )
146+ case "dev" :
147+ n , err := strconv .ParseUint (arg , 0 , 32 )
148+ if err != nil {
149+ return nil , fmt .Errorf ("invalid dev: %v" , err )
150+ }
151+ d .Dev = uint32 (n )
152+ case "qid" :
153+ q , err := parseQid (arg )
154+ if err != nil {
155+ return nil , fmt .Errorf ("invalid qid: %v" , err )
156+ }
157+ d .Qid = q
158+ case "mode" :
159+ m , err := parsePerm (arg )
160+ if err != nil {
161+ return nil , fmt .Errorf ("invalid mode: %v" , err )
162+ }
163+ d .Mode = m
164+ case "atime" :
165+ n , err := strconv .ParseUint (arg , 0 , 32 )
166+ if err != nil {
167+ return nil , fmt .Errorf ("invalid atime: %v" , err )
168+ }
169+ d .Atime = uint32 (n )
170+ case "mtime" :
171+ n , err := strconv .ParseUint (arg , 0 , 32 )
172+ if err != nil {
173+ return nil , fmt .Errorf ("invalid mtime: %v" , err )
174+ }
175+ d .Mtime = uint32 (n )
176+ case "length" :
177+ n , err := strconv .ParseUint (arg , 0 , 64 )
178+ if err != nil {
179+ return nil , fmt .Errorf ("invalid length: %v" , err )
180+ }
181+ d .Length = n
182+ case "name" :
183+ d .Name = arg
184+ case "uid" :
185+ d .Uid = arg
186+ case "gid" :
187+ d .Gid = arg
188+ case "muid" :
189+ d .Muid = arg
190+ }
191+ }
192+ return d , nil
193+ }
194+
110195func dumpsome (b []byte ) string {
196+ // Is this all directories?
197+ if s , ok := dumpDirs (b ); ok {
198+ return s
199+ }
200+
111201 if len (b ) > 64 {
112202 b = b [0 :64 ]
113203 }
@@ -126,6 +216,38 @@ func dumpsome(b []byte) string {
126216 return fmt .Sprintf ("%x" , b )
127217}
128218
219+ func dumpDirs (b []byte ) (string , bool ) {
220+ var dirs []* Dir
221+ for len (b ) > 0 {
222+ if len (b ) < 2 {
223+ return "" , false
224+ }
225+ n , _ := gbit16 (b )
226+ m := int (n ) + 2
227+ if len (b ) < m {
228+ return "" , false
229+ }
230+ d , err := UnmarshalDir (b [:m ])
231+ if err != nil {
232+ return "" , false
233+ }
234+ dirs = append (dirs , d )
235+ b = b [m :]
236+ }
237+ var out bytes.Buffer
238+ out .WriteString ("[" )
239+ for i , d := range dirs {
240+ if i > 0 {
241+ out .WriteString (" " )
242+ }
243+ out .WriteString ("(" )
244+ out .WriteString (d .String ())
245+ out .WriteString (")" )
246+ }
247+ out .WriteString ("]" )
248+ return out .String (), true
249+ }
250+
129251type Perm uint32
130252
131253type permChar struct {
@@ -164,6 +286,32 @@ var permChars = []permChar{
164286 permChar {0 , '-' },
165287}
166288
289+ func parsePerm (s string ) (Perm , error ) {
290+ orig := s
291+ did := false
292+ var p Perm
293+ for _ , pc := range permChars {
294+ if pc .bit == 0 && did {
295+ did = false
296+ continue
297+ }
298+ if s == "" {
299+ return Perm (0 ), fmt .Errorf ("perm too short: %q" , orig )
300+ }
301+ if s [0 ] == byte (pc .c ) {
302+ s = s [1 :]
303+ p |= pc .bit
304+ if pc .bit != 0 {
305+ did = true
306+ }
307+ }
308+ }
309+ if s != "" {
310+ return Perm (0 ), fmt .Errorf ("perm too long: %q" , orig )
311+ }
312+ return p , nil
313+ }
314+
167315func (p Perm ) String () string {
168316 s := ""
169317 did := false
@@ -211,7 +359,39 @@ func (q Qid) String() string {
211359 if q .Type & QTAUTH != 0 {
212360 t += "A"
213361 }
214- return fmt .Sprintf ("(%.16x %d %s)" , q .Path , q .Vers , t )
362+ if t != "" {
363+ t = "." + t
364+ }
365+ return fmt .Sprintf ("%#x.%d%s" , q .Path , q .Vers , t )
366+ }
367+
368+ func parseQid (s string ) (Qid , error ) {
369+ orig := s
370+ var q Qid
371+ var ok bool
372+ ps , vs , _ := strings .Cut (s , "." )
373+ vs , ts , _ := strings .Cut (vs , "." )
374+ pn , err1 := strconv .ParseUint (ps , 0 , 64 )
375+ vn , err2 := strconv .ParseUint (vs , 0 , 32 )
376+ if ts , ok = strings .CutPrefix (ts , "d" ); ok {
377+ q .Type |= QTDIR
378+ }
379+ if ts , ok = strings .CutPrefix (ts , "a" ); ok {
380+ q .Type |= QTAPPEND
381+ }
382+ if ts , ok = strings .CutPrefix (ts , "l" ); ok {
383+ q .Type |= QTEXCL
384+ }
385+ if ts , ok = strings .CutPrefix (ts , "A" ); ok {
386+ q .Type |= QTAUTH
387+ }
388+ if err1 != nil || err2 != nil || ts != "" {
389+ return Qid {}, fmt .Errorf ("invalid qid %q" , orig )
390+ }
391+ q .Path = pn
392+ q .Vers = uint32 (vn )
393+
394+ return q , nil
215395}
216396
217397func gqid (b []byte ) (Qid , []byte ) {
0 commit comments