xref: /inferno-os/appl/lib/styx.b (revision e45fa0eb0763b57d6fb0649c064bc3b95ccdea6c)
1implement Styx;
2
3include "sys.m";
4	sys: Sys;
5
6include "styx.m";
7
8STR: con BIT16SZ;	# string length
9TAG: con BIT16SZ;
10FID: con BIT32SZ;
11QID: con BIT8SZ+BIT32SZ+BIT64SZ;
12LEN: con BIT16SZ;	# stat and qid array lengths
13COUNT: con BIT32SZ;
14OFFSET: con BIT64SZ;
15
16H: con BIT32SZ+BIT8SZ+BIT16SZ;	# minimum header length: size[4] type tag[2]
17
18#
19# the following array could be shorter if it were indexed by (type-Tversion)
20#
21hdrlen := array[Tmax] of
22{
23Tversion =>	H+COUNT+STR,	# size[4] Tversion tag[2] msize[4] version[s]
24Rversion =>	H+COUNT+STR,	# size[4] Rversion tag[2] msize[4] version[s]
25
26Tauth =>	H+FID+STR+STR,		# size[4] Tauth tag[2] afid[4] uname[s] aname[s]
27Rauth =>	H+QID,			# size[4] Rauth tag[2] aqid[13]
28
29Rerror =>	H+STR,		# size[4] Rerror tag[2] ename[s]
30
31Tflush =>	H+TAG,		# size[4] Tflush tag[2] oldtag[2]
32Rflush =>	H,			# size[4] Rflush tag[2]
33
34Tattach =>	H+FID+FID+STR+STR,	# size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
35Rattach =>	H+QID,		# size[4] Rattach tag[2] qid[13]
36
37Twalk =>	H+FID+FID+LEN,	# size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
38Rwalk =>	H+LEN,		# size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
39
40Topen =>	H+FID+BIT8SZ,		# size[4] Topen tag[2] fid[4] mode[1]
41Ropen =>	H+QID+COUNT,	# size[4] Ropen tag[2] qid[13] iounit[4]
42
43Tcreate =>	H+FID+STR+BIT32SZ+BIT8SZ,	# size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
44Rcreate =>	H+QID+COUNT,	# size[4] Rcreate tag[2] qid[13] iounit[4]
45
46Tread =>	H+FID+OFFSET+COUNT,	# size[4] Tread tag[2] fid[4] offset[8] count[4]
47Rread =>	H+COUNT,		# size[4] Rread tag[2] count[4] data[count]
48
49Twrite =>	H+FID+OFFSET+COUNT,	# size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
50Rwrite =>	H+COUNT,	# size[4] Rwrite tag[2] count[4]
51
52Tclunk =>	H+FID,	# size[4] Tclunk tag[2] fid[4]
53Rclunk =>	H,		# size[4] Rclunk tag[2]
54
55Tremove =>	H+FID,	# size[4] Tremove tag[2] fid[4]
56Rremove =>	H,	# size[4] Rremove tag[2]
57
58Tstat =>	H+FID,	# size[4] Tstat tag[2] fid[4]
59Rstat =>	H+LEN,	# size[4] Rstat tag[2] stat[n]
60
61Twstat =>	H+FID+LEN,	# size[4] Twstat tag[2] fid[4] stat[n]
62Rwstat =>	H,	# size[4] Rwstat tag[2]
63};
64
65init()
66{
67	sys = load Sys Sys->PATH;
68}
69
70utflen(s: string): int
71{
72	# the domain is 21-bit unicode
73	n := l := len s;
74	for(i:=0; i<l; i++)
75		if((c := s[i]) > 16r7F){
76			n++;
77			if(c > 16r7FF){
78				n++;
79				if(c > 16rFFFF)
80					n++;
81			}
82		}
83	return n;
84}
85
86packdirsize(d: Sys->Dir): int
87{
88	return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid);
89}
90
91packdir(f: Sys->Dir): array of byte
92{
93	ds := packdirsize(f);
94	a := array[ds] of byte;
95	# size[2]
96	a[0] = byte (ds-LEN);
97	a[1] = byte ((ds-LEN)>>8);
98	# type[2]
99	a[2] = byte f.dtype;
100	a[3] = byte (f.dtype>>8);
101	# dev[4]
102	a[4] = byte f.dev;
103	a[5] = byte (f.dev>>8);
104	a[6] = byte (f.dev>>16);
105	a[7] = byte (f.dev>>24);
106	# qid.type[1]
107	# qid.vers[4]
108	# qid.path[8]
109	pqid(a, 8, f.qid);
110	# mode[4]
111	a[21] = byte f.mode;
112	a[22] = byte (f.mode>>8);
113	a[23] = byte (f.mode>>16);
114	a[24] = byte (f.mode>>24);
115	# atime[4]
116	a[25] = byte f.atime;
117	a[26] = byte (f.atime>>8);
118	a[27] = byte (f.atime>>16);
119	a[28] = byte (f.atime>>24);
120	# mtime[4]
121	a[29] = byte f.mtime;
122	a[30] = byte (f.mtime>>8);
123	a[31] = byte (f.mtime>>16);
124	a[32] = byte (f.mtime>>24);
125	# length[8]
126	p64(a, 33, big f.length);
127	# name[s]
128	i := pstring(a, 33+BIT64SZ, f.name);
129	i = pstring(a, i, f.uid);
130	i = pstring(a, i, f.gid);
131	i = pstring(a, i, f.muid);
132	if(i != len a)
133		raise "assertion: Styx->packdir: bad count";	# can't happen unless packedsize is wrong
134	return a;
135}
136
137pqid(a: array of byte, o: int, q: Sys->Qid): int
138{
139	a[o] = byte q.qtype;
140	v := q.vers;
141	a[o+1] = byte v;
142	a[o+2] = byte (v>>8);
143	a[o+3] = byte (v>>16);
144	a[o+4] = byte (v>>24);
145	v = int q.path;
146	a[o+5] = byte v;
147	a[o+6] = byte (v>>8);
148	a[o+7] = byte (v>>16);
149	a[o+8] = byte (v>>24);
150	v = int (q.path >> 32);
151	a[o+9] = byte v;
152	a[o+10] = byte (v>>8);
153	a[o+11] = byte (v>>16);
154	a[o+12] = byte (v>>24);
155	return o+QID;
156}
157
158pstring(a: array of byte, o: int, s: string): int
159{
160	sa := array of byte s;	# could do conversion ourselves
161	n := len sa;
162	a[o] = byte n;
163	a[o+1] = byte (n>>8);
164	a[o+2:] = sa;
165	return o+LEN+n;
166}
167
168p32(a: array of byte, o: int, v: int): int
169{
170	a[o] = byte v;
171	a[o+1] = byte (v>>8);
172	a[o+2] = byte (v>>16);
173	a[o+3] = byte (v>>24);
174	return o+BIT32SZ;
175}
176
177p64(a: array of byte, o: int, b: big): int
178{
179	i := int b;
180	a[o] = byte i;
181	a[o+1] = byte (i>>8);
182	a[o+2] = byte (i>>16);
183	a[o+3] = byte (i>>24);
184	i = int (b>>32);
185	a[o+4] = byte i;
186	a[o+5] = byte (i>>8);
187	a[o+6] = byte (i>>16);
188	a[o+7] = byte (i>>24);
189	return o+BIT64SZ;
190}
191
192unpackdir(a: array of byte): (int, Sys->Dir)
193{
194	dir: Sys->Dir;
195
196	if(len a < STATFIXLEN)
197		return (0, dir);
198	# size[2]
199	sz := ((int a[1] << 8) | int a[0])+LEN;	# bytes this packed dir should occupy
200	if(len a < sz)
201		return (0, dir);
202	# type[2]
203	dir.dtype = (int a[3]<<8) | int a[2];
204	# dev[4]
205	dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4];
206	# qid.type[1]
207	# qid.vers[4]
208	# qid.path[8]
209	dir.qid = gqid(a, 8);
210	# mode[4]
211	dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21];
212	# atime[4]
213	dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25];
214	# mtime[4]
215	dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29];
216	# length[8]
217	v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33];
218	v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37];
219	dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF);
220	# name[s], uid[s], gid[s], muid[s]
221	i: int;
222	(dir.name, i) = gstring(a, 41);
223	(dir.uid, i) = gstring(a, i);
224	(dir.gid, i) = gstring(a, i);
225	(dir.muid, i) = gstring(a, i);
226	if(i != sz)
227		return (0, dir);
228	return (i, dir);
229}
230
231gqid(f: array of byte, i: int): Sys->Qid
232{
233	qtype := int f[i];
234	vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1];
235	i += BIT8SZ+BIT32SZ;
236	path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
237	i += BIT32SZ;
238	path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
239	path := (big path1 << 32) | (big path0 & 16rFFFFFFFF);
240	return (path, vers, qtype);
241}
242
243g32(f: array of byte, i: int): int
244{
245	return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
246}
247
248g64(f: array of byte, i: int): big
249{
250	b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
251	b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4];
252	return (big b1 << 32) | (big b0 & 16rFFFFFFFF);
253}
254
255gstring(a: array of byte, o: int): (string, int)
256{
257	if(o < 0 || o+STR > len a)
258		return (nil, -1);
259	l := (int a[o+1] << 8) | int a[o];
260	o += STR;
261	e := o+l;
262	if(e > len a)
263		return (nil, -1);
264	return (string a[o:e], e);
265}
266
267ttag2type := array[] of {
268tagof Tmsg.Readerror => 0,
269tagof Tmsg.Version => Tversion,
270tagof Tmsg.Auth => Tauth,
271tagof Tmsg.Attach => Tattach,
272tagof Tmsg.Flush => Tflush,
273tagof Tmsg.Walk => Twalk,
274tagof Tmsg.Open => Topen,
275tagof Tmsg.Create => Tcreate,
276tagof Tmsg.Read => Tread,
277tagof Tmsg.Write => Twrite,
278tagof Tmsg.Clunk => Tclunk,
279tagof Tmsg.Stat => Tstat,
280tagof Tmsg.Remove => Tremove,
281tagof Tmsg.Wstat => Twstat,
282};
283
284Tmsg.mtype(t: self ref Tmsg): int
285{
286	return ttag2type[tagof t];
287}
288
289Tmsg.packedsize(t: self ref Tmsg): int
290{
291	mtype := ttag2type[tagof t];
292	if(mtype <= 0)
293		return 0;
294	ml := hdrlen[mtype];
295	pick m := t {
296	Version =>
297		ml += utflen(m.version);
298	Auth =>
299		ml += utflen(m.uname)+utflen(m.aname);
300	Attach =>
301		ml += utflen(m.uname)+utflen(m.aname);
302	Walk =>
303		for(i:=0; i<len m.names; i++)
304			ml += STR+utflen(m.names[i]);
305	Create =>
306		ml += utflen(m.name);
307	Write =>
308		ml += len m.data;
309	Wstat =>
310		ml += packdirsize(m.stat);
311	}
312	return ml;
313}
314
315Tmsg.pack(t: self ref Tmsg): array of byte
316{
317	if(t == nil)
318		return nil;
319	ds := t.packedsize();
320	if(ds <= 0)
321		return nil;
322	d := array[ds] of byte;
323	d[0] = byte ds;
324	d[1] = byte (ds>>8);
325	d[2] = byte (ds>>16);
326	d[3] = byte (ds>>24);
327	d[4] = byte ttag2type[tagof t];
328	d[5] = byte t.tag;
329	d[6] = byte (t.tag >> 8);
330	pick m := t {
331	Version =>
332		p32(d, H, m.msize);
333		pstring(d, H+COUNT, m.version);
334	Auth =>
335		p32(d, H, m.afid);
336		o := pstring(d, H+FID, m.uname);
337		pstring(d, o, m.aname);
338	Flush =>
339		v := m.oldtag;
340		d[H] = byte v;
341		d[H+1] = byte (v>>8);
342	Attach =>
343		p32(d, H, m.fid);
344		p32(d, H+FID, m.afid);
345		o := pstring(d, H+2*FID, m.uname);
346		pstring(d, o, m.aname);
347	Walk =>
348		d[H] = byte m.fid;
349		d[H+1] = byte (m.fid>>8);
350		d[H+2] = byte (m.fid>>16);
351		d[H+3] = byte (m.fid>>24);
352		d[H+FID] = byte m.newfid;
353		d[H+FID+1] = byte (m.newfid>>8);
354		d[H+FID+2] = byte (m.newfid>>16);
355		d[H+FID+3] = byte (m.newfid>>24);
356		n := len m.names;
357		d[H+2*FID] = byte n;
358		d[H+2*FID+1] = byte (n>>8);
359		o := H+2*FID+LEN;
360		for(i := 0; i < n; i++)
361			o = pstring(d, o, m.names[i]);
362	Open =>
363		p32(d, H, m.fid);
364		d[H+FID] = byte m.mode;
365	Create =>
366		p32(d, H, m.fid);
367		o := pstring(d, H+FID, m.name);
368		p32(d, o, m.perm);
369		d[o+BIT32SZ] = byte m.mode;
370	Read =>
371		p32(d, H, m.fid);
372		p64(d, H+FID, m.offset);
373		p32(d, H+FID+OFFSET, m.count);
374	Write =>
375		p32(d, H, m.fid);
376		p64(d, H+FID, m.offset);
377		n := len m.data;
378		p32(d, H+FID+OFFSET, n);
379		d[H+FID+OFFSET+COUNT:] = m.data;
380	Clunk or Remove or Stat =>
381		p32(d, H, m.fid);
382	Wstat =>
383		p32(d, H, m.fid);
384		stat := packdir(m.stat);
385		n := len stat;
386		d[H+FID] = byte n;
387		d[H+FID+1] = byte (n>>8);
388		d[H+FID+LEN:] = stat;
389	* =>
390		raise sys->sprint("assertion: Styx->Tmsg.pack: bad tag: %d", tagof t);
391	}
392	return d;
393}
394
395Tmsg.unpack(f: array of byte): (int, ref Tmsg)
396{
397	if(len f < H)
398		return (0, nil);
399	size := (int f[1] << 8) | int f[0];
400	size |= ((int f[3] << 8) | int f[2]) << 16;
401	if(len f != size){
402		if(len f < size)
403			return (0, nil);	# need more data
404		f = f[0:size];	# trim to exact length
405	}
406	mtype := int f[4];
407	if(mtype >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype])
408		return (-1, nil);
409
410	tag := (int f[6] << 8) | int f[5];
411	fid := 0;
412	if(hdrlen[mtype] >= H+FID)
413		fid = g32(f, H);	# fid is always in same place: extract it once for all if there
414
415	# return out of each case body for a legal message;
416	# break out of the case for an illegal one
417
418Decode:
419	case mtype {
420	* =>
421		sys->print("styx: Tmsg.unpack: bad type %d\n", mtype);
422	Tversion =>
423		msize := fid;
424		(version, o) := gstring(f, H+COUNT);
425		if(o <= 0)
426			break;
427		return (o, ref Tmsg.Version(tag, msize, version));
428	Tauth =>
429		(uname, o1) := gstring(f, H+FID);
430		(aname, o2) := gstring(f, o1);
431		if(o2 <= 0)
432			break;
433		return (o2, ref Tmsg.Auth(tag, fid, uname, aname));
434	Tflush =>
435		oldtag := (int f[H+1] << 8) | int f[H];
436		return (H+TAG, ref Tmsg.Flush(tag, oldtag));
437	Tattach =>
438		afid := g32(f, H+FID);
439		(uname, o1) := gstring(f, H+2*FID);
440		(aname, o2) := gstring(f, o1);
441		if(o2 <= 0)
442			break;
443		return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname));
444	Twalk =>
445		newfid := g32(f, H+FID);
446		n := (int f[H+2*FID+1] << 8) | int f[H+2*FID];
447		if(n > MAXWELEM)
448			break;
449		o := H+2*FID+LEN;
450		names: array of string = nil;
451		if(n > 0){
452			names = array[n] of string;
453			for(i:=0; i<n; i++){
454				(names[i], o) = gstring(f, o);
455				if(o <= 0)
456					break Decode;
457			}
458		}
459		return (o, ref Tmsg.Walk(tag, fid, newfid, names));
460	Topen =>
461		return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID]));
462	Tcreate =>
463		(name, o) := gstring(f, H+FID);
464		if(o <= 0 || o+BIT32SZ+BIT8SZ > len f)
465			break;
466		perm := g32(f, o);
467		o += BIT32SZ;
468		mode := int f[o++];
469		return (o, ref Tmsg.Create(tag, fid, name, perm, mode));
470	Tread =>
471		offset := g64(f, H+FID);
472		count := g32(f, H+FID+OFFSET);
473		return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count));
474	Twrite =>
475		offset := g64(f, H+FID);
476		count := g32(f, H+FID+OFFSET);
477		O: con H+FID+OFFSET+COUNT;
478		if(count > len f-O)
479			break;
480		data := f[O:O+count];
481		return (O+count, ref Tmsg.Write(tag, fid, offset, data));
482	Tclunk =>
483		return (H+FID, ref Tmsg.Clunk(tag, fid));
484	Tremove =>
485		return (H+FID, ref Tmsg.Remove(tag, fid));
486	Tstat =>
487		return (H+FID, ref Tmsg.Stat(tag, fid));
488	Twstat =>
489		n := int (f[H+FID+1]<<8) | int f[H+FID];
490		if(len f < H+FID+LEN+n)
491			break;
492		(ds, stat) := unpackdir(f[H+FID+LEN:]);
493		if(ds != n){
494			sys->print("Styx->Tmsg.unpack: wstat count: %d/%d\n", ds, n);	# temporary
495			break;
496		}
497		return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat));
498	}
499	return (-1, nil);		# illegal
500}
501
502tmsgname := array[] of {
503tagof Tmsg.Readerror => "Readerror",
504tagof Tmsg.Version => "Version",
505tagof Tmsg.Auth => "Auth",
506tagof Tmsg.Attach => "Attach",
507tagof Tmsg.Flush => "Flush",
508tagof Tmsg.Walk => "Walk",
509tagof Tmsg.Open => "Open",
510tagof Tmsg.Create => "Create",
511tagof Tmsg.Read => "Read",
512tagof Tmsg.Write => "Write",
513tagof Tmsg.Clunk => "Clunk",
514tagof Tmsg.Stat => "Stat",
515tagof Tmsg.Remove => "Remove",
516tagof Tmsg.Wstat => "Wstat",
517};
518
519Tmsg.text(t: self ref Tmsg): string
520{
521	if(t == nil)
522		return "nil";
523	s := sys->sprint("Tmsg.%s(%ud", tmsgname[tagof t], t.tag);
524	pick m:= t {
525	* =>
526		return s + ",ILLEGAL)";
527	Readerror =>
528		return s + sys->sprint(",\"%s\")", m.error);
529	Version =>
530		return s + sys->sprint(",%d,\"%s\")", m.msize, m.version);
531	Auth =>
532		return s + sys->sprint(",%ud,\"%s\",\"%s\")", m.afid, m.uname, m.aname);
533	Flush =>
534		return s + sys->sprint(",%ud)", m.oldtag);
535	Attach =>
536		return s + sys->sprint(",%ud,%ud,\"%s\",\"%s\")", m.fid, m.afid, m.uname, m.aname);
537	Walk =>
538		s += sys->sprint(",%ud,%ud", m.fid, m.newfid);
539		if(len m.names != 0){
540			s += ",array[] of {";
541			for(i := 0; i < len m.names; i++){
542				c := ",";
543				if(i == 0)
544					c = "";
545				s += sys->sprint("%s\"%s\"", c, m.names[i]);
546			}
547			s += "}";
548		}else
549			s += ",nil";
550		return s + ")";
551	Open =>
552		return s + sys->sprint(",%ud,%d)", m.fid, m.mode);
553	Create =>
554		return s + sys->sprint(",%ud,\"%s\",8r%uo,%d)", m.fid, m.name, m.perm, m.mode);
555	Read =>
556		return s + sys->sprint(",%ud,%bd,%ud)", m.fid, m.offset, m.count);
557	Write =>
558		return s + sys->sprint(",%ud,%bd,array[%d] of byte)", m.fid, m.offset, len m.data);
559	Clunk or
560	Remove or
561	Stat =>
562		return s + sys->sprint(",%ud)", m.fid);
563	Wstat =>
564		return s + sys->sprint(",%ud,%s)", m.fid, dir2text(m.stat));
565	}
566}
567
568Tmsg.read(fd: ref Sys->FD, msglim: int): ref Tmsg
569{
570	(msg, err) := readmsg(fd, msglim);
571	if(err != nil)
572		return ref Tmsg.Readerror(0, err);
573	if(msg == nil)
574		return nil;
575	(nil, m) := Tmsg.unpack(msg);
576	if(m == nil)
577		return ref Tmsg.Readerror(0, "bad 9P T-message format");
578	return m;
579}
580
581rtag2type := array[] of {
582tagof Rmsg.Version	=> Rversion,
583tagof Rmsg.Auth	=> Rauth,
584tagof Rmsg.Error	=> Rerror,
585tagof Rmsg.Flush	=> Rflush,
586tagof Rmsg.Attach	=> Rattach,
587tagof Rmsg.Walk	=> Rwalk,
588tagof Rmsg.Open	=> Ropen,
589tagof Rmsg.Create	=> Rcreate,
590tagof Rmsg.Read	=> Rread,
591tagof Rmsg.Write	=> Rwrite,
592tagof Rmsg.Clunk	=> Rclunk,
593tagof Rmsg.Remove	=> Rremove,
594tagof Rmsg.Stat	=> Rstat,
595tagof Rmsg.Wstat	=> Rwstat,
596};
597
598Rmsg.mtype(r: self ref Rmsg): int
599{
600	return rtag2type[tagof r];
601}
602
603Rmsg.packedsize(r: self ref Rmsg): int
604{
605	mtype := rtag2type[tagof r];
606	if(mtype <= 0)
607		return 0;
608	ml := hdrlen[mtype];
609	pick m := r {
610	Version =>
611		ml += utflen(m.version);
612	Error =>
613		ml += utflen(m.ename);
614	Walk =>
615		ml += QID*len m.qids;
616	Read =>
617		ml += len m.data;
618	Stat =>
619		ml += packdirsize(m.stat);
620	}
621	return ml;
622}
623
624Rmsg.pack(r: self ref Rmsg): array of byte
625{
626	if(r == nil)
627		return nil;
628	ps := r.packedsize();
629	if(ps <= 0)
630		return nil;
631	d := array[ps] of byte;
632	d[0] = byte ps;
633	d[1] = byte (ps>>8);
634	d[2] = byte (ps>>16);
635	d[3] = byte (ps>>24);
636	d[4] = byte rtag2type[tagof r];
637	d[5] = byte r.tag;
638	d[6] = byte (r.tag >> 8);
639	pick m := r {
640	Version =>
641		p32(d, H, m.msize);
642		pstring(d, H+BIT32SZ, m.version);
643	Auth =>
644		pqid(d, H, m.aqid);
645	Flush or
646	Clunk or
647	Remove or
648	Wstat =>
649		;	# nothing more required
650	Error	=>
651		pstring(d, H, m.ename);
652	Attach =>
653		pqid(d, H, m.qid);
654	Walk =>
655		n := len m.qids;
656		d[H] = byte n;
657		d[H+1] = byte (n>>8);
658		o := H+LEN;
659		for(i:=0; i<n; i++){
660			pqid(d, o, m.qids[i]);
661			o += QID;
662		}
663	Create or
664	Open =>
665		pqid(d, H, m.qid);
666		p32(d, H+QID, m.iounit);
667	Read =>
668		v := len m.data;
669		d[H] = byte v;
670		d[H+1] = byte (v>>8);
671		d[H+2] = byte (v>>16);
672		d[H+3] = byte (v>>24);
673		d[H+4:] = m.data;
674	Write =>
675		v := m.count;
676		d[H] = byte v;
677		d[H+1] = byte (v>>8);
678		d[H+2] = byte (v>>16);
679		d[H+3] = byte (v>>24);
680	Stat =>
681		stat := packdir(m.stat);
682		v := len stat;
683		d[H] = byte v;
684		d[H+1] = byte (v>>8);
685		d[H+2:] = stat;		# should avoid copy?
686	* =>
687		raise sys->sprint("assertion: Styx->Rmsg.pack: missed case: tag %d", tagof r);
688	}
689	return d;
690}
691
692Rmsg.unpack(f: array of byte): (int, ref Rmsg)
693{
694	if(len f < H)
695		return (0, nil);
696	size := (int f[1] << 8) | int f[0];
697	size |= ((int f[3] << 8) | int f[2]) << 16;	# size includes itself
698	if(len f != size){
699		if(len f < size)
700			return (0, nil);	# need more data
701		f = f[0:size];	# trim to exact length
702	}
703	mtype := int f[4];
704	if(mtype >= len hdrlen || (mtype&1) == 0 || size < hdrlen[mtype])
705		return (-1, nil);
706
707	tag := (int f[6] << 8) | int f[5];
708
709	# return out of each case body for a legal message;
710	# break out of the case for an illegal one
711
712	case mtype {
713	* =>
714		sys->print("Styx->Rmsg.unpack: bad type %d\n", mtype);	# temporary
715	Rversion =>
716		msize := g32(f, H);
717		(version, o) := gstring(f, H+BIT32SZ);
718		if(o <= 0)
719			break;
720		return (o, ref Rmsg.Version(tag, msize, version));
721	Rauth =>
722		return (H+QID, ref Rmsg.Auth(tag, gqid(f, H)));
723	Rflush =>
724		return (H, ref Rmsg.Flush(tag));
725	Rerror =>
726		(ename, o) := gstring(f, H);
727		if(o <= 0)
728			break;
729		return (o, ref Rmsg.Error(tag, ename));
730	Rclunk =>
731		return (H, ref Rmsg.Clunk(tag));
732	Rremove =>
733		return (H, ref Rmsg.Remove(tag));
734	Rwstat=>
735		return (H, ref Rmsg.Wstat(tag));
736	Rattach =>
737		return (H+QID, ref Rmsg.Attach(tag, gqid(f, H)));
738	Rwalk =>
739		nqid := (int f[H+1] << 8) | int f[H];
740		if(len f < H+LEN+nqid*QID)
741			break;
742		o := H+LEN;
743		qids := array[nqid] of Sys->Qid;
744		for(i:=0; i<nqid; i++){
745			qids[i] = gqid(f, o);
746			o += QID;
747		}
748		return (o, ref Rmsg.Walk(tag, qids));
749	Ropen =>
750		return (H+QID+COUNT, ref Rmsg.Open(tag, gqid(f, H), g32(f, H+QID)));
751	Rcreate=>
752		return (H+QID+COUNT, ref Rmsg.Create(tag, gqid(f, H), g32(f, H+QID)));
753	Rread =>
754		count := g32(f, H);
755		if(len f < H+COUNT+count)
756			break;
757		data := f[H+COUNT:H+COUNT+count];
758		return (H+COUNT+count, ref Rmsg.Read(tag, data));
759	Rwrite =>
760		return (H+COUNT, ref Rmsg.Write(tag, g32(f, H)));
761	Rstat =>
762		n := (int f[H+1] << 8) | int f[H];
763		if(len f < H+LEN+n)
764			break;
765		(ds, d) := unpackdir(f[H+LEN:]);
766		if(ds <= 0)
767			break;
768		if(ds != n){
769			sys->print("Styx->Rmsg.unpack: stat count: %d/%d\n", ds, n);		# temporary
770			break;
771		}
772		return (H+LEN+n, ref Rmsg.Stat(tag, d));
773	}
774	return (-1, nil);		# illegal
775}
776
777rmsgname := array[] of {
778tagof Rmsg.Version => "Version",
779tagof Rmsg.Auth => "Auth",
780tagof Rmsg.Attach => "Attach",
781tagof Rmsg.Error => "Error",
782tagof Rmsg.Flush => "Flush",
783tagof Rmsg.Walk => "Walk",
784tagof Rmsg.Create => "Create",
785tagof Rmsg.Open => "Open",
786tagof Rmsg.Read => "Read",
787tagof Rmsg.Write => "Write",
788tagof Rmsg.Clunk => "Clunk",
789tagof Rmsg.Remove => "Remove",
790tagof Rmsg.Stat => "Stat",
791tagof Rmsg.Wstat => "Wstat",
792};
793
794Rmsg.text(r: self ref Rmsg): string
795{
796	if(sys == nil)
797		sys = load Sys Sys->PATH;
798	if(r == nil)
799		return "nil";
800	s := sys->sprint("Rmsg.%s(%ud", rmsgname[tagof r], r.tag);
801	pick m := r {
802	* =>
803		return s + "ERROR)";
804	Readerror =>
805		return s + sys->sprint(",\"%s\")", m.error);
806	Version =>
807		return s + sys->sprint(",%d,\"%s\")", m.msize, m.version);
808	Auth =>
809		return s+sys->sprint(",%s)", qid2text(m.aqid));
810	Error =>
811		return s+sys->sprint(",\"%s\")", m.ename);
812	Flush or
813	Clunk or
814	Remove or
815	Wstat =>
816		return s+")";
817	Attach =>
818		return s+sys->sprint(",%s)", qid2text(m.qid));
819	Walk	 =>
820		s += ",array[] of {";
821		for(i := 0; i < len m.qids; i++){
822			c := "";
823			if(i != 0)
824				c = ",";
825			s += sys->sprint("%s%s", c, qid2text(m.qids[i]));
826		}
827		return s+"})";
828	Create or
829	Open =>
830		return s+sys->sprint(",%s,%d)", qid2text(m.qid), m.iounit);
831	Read =>
832		return s+sys->sprint(",array[%d] of byte)", len m.data);
833	Write =>
834		return s+sys->sprint(",%d)", m.count);
835	Stat =>
836		return s+sys->sprint(",%s)", dir2text(m.stat));
837	}
838}
839
840Rmsg.read(fd: ref Sys->FD, msglim: int): ref Rmsg
841{
842	(msg, err) := readmsg(fd, msglim);
843	if(err != nil)
844		return ref Rmsg.Readerror(0, err);
845	if(msg == nil)
846		return nil;
847	(nil, m) := Rmsg.unpack(msg);
848	if(m == nil)
849		return ref Rmsg.Readerror(0, "bad 9P R-message format");
850	return m;
851}
852
853dir2text(d: Sys->Dir): string
854{
855	return sys->sprint("Dir(\"%s\",\"%s\",\"%s\",%s,8r%uo,%d,%d,%bd,16r%ux,%d)",
856		d.name, d.uid, d.gid, qid2text(d.qid), d.mode, d.atime, d.mtime, d.length, d.dtype, d.dev);
857}
858
859qid2text(q: Sys->Qid): string
860{
861	return sys->sprint("Qid(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype);
862}
863
864readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string)
865{
866	if(msglim <= 0)
867		msglim = MAXRPC;
868	sbuf := array[BIT32SZ] of byte;
869	if((n := sys->readn(fd, sbuf, BIT32SZ)) != BIT32SZ){
870		if(n == 0)
871			return (nil, nil);
872		return (nil, sys->sprint("%r"));
873	}
874	ml := (int sbuf[1] << 8) | int sbuf[0];
875	ml |= ((int sbuf[3] << 8) | int sbuf[2]) << 16;
876	if(ml <= BIT32SZ)
877		return (nil, "invalid 9P message size");
878	if(ml > msglim)
879		return (nil, "9P message longer than agreed");
880	buf := array[ml] of byte;
881	buf[0:] = sbuf;
882	if((n = sys->readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){
883		if(n == 0)
884			return (nil, "9P message truncated");
885		return (nil, sys->sprint("%r"));
886	}
887	return (buf, nil);
888}
889
890istmsg(f: array of byte): int
891{
892	if(len f < H)
893		return -1;
894	return (int f[BIT32SZ] & 1) == 0;
895}
896
897compatible(t: ref Tmsg.Version, msize: int, version: string): (int, string)
898{
899	if(version == nil)
900		version = VERSION;
901	if(t.msize < msize)
902		msize = t.msize;
903	v := t.version;
904	if(len v < 2 || v[0:2] != "9P")
905		return (msize, "unknown");
906	for(i:=2; i<len v; i++)
907		if((c := v[i]) == '.'){
908			v = v[0:i];
909			break;
910		}else if(!(c >= '0' && c <= '9'))
911			return (msize, "unknown");	# fussier than Plan 9
912	if(v < VERSION)
913		return (msize, "unknown");
914	if(v < version)
915		version = v;
916	return (msize, version);
917}
918
919# only here to support an implementation of this module that talks the previous version of Styx
920write(fd: ref Sys->FD, buf: array of byte, nb: int): int
921{
922	return sys->write(fd, buf, nb);
923}
924