Charles.Forsyth20080116-1340f4bb33e18 years agoHistory
557 lines · 11.7 KB · Brainfuck
RawDownload
implement Styxchat; ## Copyright © 2002,2003 Vita Nuova Holdings Limited.  All rights reserved.# include "sys.m";	sys: Sys; include "draw.m"; include "styx.m";	styx: Styx;	Tmsg, Rmsg: import styx; include "string.m";	str: String; include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "arg.m"; Styxchat: module{	init:	fn(nil: ref Draw->Context, nil: list of string);}; msgsize := 64*1024;nexttag := 1;verbose := 0; stdin: ref Sys->FD; init(nil: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	styx = load Styx Styx->PATH;	str = load String String->PATH;	bufio = load Bufio Bufio->PATH;	styx->init(); 	client := 1;	addr := 0;	arg := load Arg Arg->PATH;	arg->init(args);	arg->setusage("styxchat [-nsv] [-m messagesize] [dest]");	while((o := arg->opt()) != 0)		case o {		'm' =>			msgsize = atoi(arg->earg());		's' =>			client = 0;		'n' =>			addr = 1;		'v' =>			verbose++;		* =>			arg->usage();		}	args = arg->argv();	arg = nil;	fd: ref Sys->FD;	if(args == nil){		fd = sys->fildes(0);		stdin = sys->open("/dev/cons", Sys->ORDWR);		if (stdin == nil)			err(sys->sprint("can't open /dev/cons: %r"));		sys->dup(stdin.fd, 1);	}else{		if(tl args != nil)			arg->usage();		stdin = sys->fildes(0);		dest := hd args;		if(addr){			dest = netmkaddr(dest, "net", "styx");			if (client){				(rc, c) := sys->dial(dest, nil);				if(rc < 0)					err(sys->sprint("can't dial %s: %r", dest));				fd = c.dfd;			}else{				(rlc, lc) := sys->announce(dest);				if (rlc < 0)					err(sys->sprint("can't announce %s: %r", dest));				(rc, c) := sys->listen(lc);				if (rc < 0)					err(sys->sprint("can't listen on %s: %r", dest));				fd = sys->open(c.dir + "/data", Sys->ORDWR);				if (fd == nil)					err(sys->sprint("can't open %s/data: %r", c.dir));			}		}else{			fd = sys->open(dest, Sys->ORDWR);			if(fd == nil)				err(sys->sprint("can't open %s: %r", dest));		}	}	sys->pctl(Sys->NEWPGRP, nil);	if(client){		spawn Rreader(fd);		Twriter(fd);	}else{		spawn Treader(fd);		Rwriter(fd);	}} netmkaddr(addr, net, svc: string): string{	if(net == nil)		net = "net";	(n, nil) := sys->tokenize(addr, "!");	if(n <= 1){		if(svc== nil)			return sys->sprint("%s!%s", net, addr);		return sys->sprint("%s!%s!%s", net, addr, svc);	}	if(svc == nil || n > 2)		return addr;	return sys->sprint("%s!%s", addr, svc);} quit(e: int){	fd := sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE);	if(fd != nil)		sys->fprint(fd, "killgrp");	if(e)		raise "fail:error";	exit;} Rreader(fd: ref Sys->FD){	while((m := Rmsg.read(fd, msgsize)) != nil){		sys->print("<- %s\n%s", m.text(), Rdump(m));		if(tagof m == tagof Rmsg.Readerror)			quit(1);	}	sys->print("styxchat: server hungup\n");} Twriter(fd: ref Sys->FD){	in := bufio->fopen(stdin, Sys->OREAD);	while((l := in.gets('\n')) != nil){		if(l != nil && l[0] == '#')			continue;		(t, err) := Tparse(l);		if(t == nil){			if(err != nil)				sys->print("?%s\n", err);		}else{			if(t.tag == 0)				t.tag = nexttag;			a := t.pack();			if(a != nil){				sys->print("-> %s\n%s", t.text(), Tdump(t));				n := len a;				if(n <= msgsize){					if(sys->write(fd, a, len a) != len a)						sys->print("?write error to server: %r\n");					if(t.tag != Styx->NOTAG && t.tag != ~0)						nexttag++;				}else					sys->print("?message bigger than agreed: %d bytes\n", n);			}else				sys->fprint(sys->fildes(2), "styxchat: T-message conversion failed\n");		}	}} Rdump(m: ref Rmsg): string{	if(!verbose)		return "";	pick r :=m {	Read =>		return dump(r.data, len r.data, verbose>1);	* =>		return "";	}} Tdump(m: ref Tmsg): string{	if(!verbose)		return "";	pick t := m {	Write =>		return dump(t.data, len t.data, verbose>1);	* =>		return "";	}} isprint(c: int): int{	return c >= 16r20 && c < 16r7F || c == '\n' || c == '\t' || c == '\r';} textdump(a: array of byte, lim: int): string{	s := "\ttext(\"";	for(i := 0; i < lim; i++)		case c := int a[i] {		'\t' =>			s += "\\t";		'\n' =>			s += "\\n";		'\r' =>			s += "\\r";		'"' =>			s += "\\\"";		* =>			if(isprint(c))				s[len s] = c;			else				s += sys->sprint("\\u%4.4ux", c);		}	s += "\")\n";	return s;} dump(a: array of byte, lim: int, text: int): string{	if(a == nil)		return "";	if(len a < lim)		lim = len a;	printable := 1;	for(i := 0; i < lim; i++)		if(!isprint(int a[i])){			printable = 0;			break;		}	if(printable)		return textdump(a, lim);	s := "\tdump(";	for(i = 0; i < lim; i++)		s += sys->sprint("%2.2ux", int a[i]);	s += ")\n";	if(text)		s += textdump(a, lim);	return s;} val(s: string): int{	if(s == "~0")		return ~0;	return atoi(s);} bigval(s: string): big{	if(s == "~0")		return ~ big 0;	return atob(s);} fid(s: string): int{	if(s == "nofid" || s == "NOFID")		return Styx->NOFID;	return val(s);} tag(s: string): int{	if(s == "~0" || s == "notag" || s == "NOTAG")		return Styx->NOTAG;	return atoi(s);} dir(name: string, uid: string, gid: string, mode: int, mtime: int, length: big): Sys->Dir{	d := sys->zerodir;	d.name = name;	d.uid = uid;	d.gid = gid;	d.mode = mode;	d.mtime = mtime;	d.length = length;	return d;} Tparse(s: string): (ref Tmsg, string){	args := str->unquoted(s);	if(args == nil)		return (nil, nil);	argc := len args;	av := array[argc] of string;	for(i:=0; args != nil; args = tl args)		av[i++] = hd args;	case av[0] {	"Tversion" =>		if(argc != 3)			return (nil, "usage: Tversion messagesize version");		return (ref Tmsg.Version(Styx->NOTAG, atoi(av[1]), av[2]), nil);	"Tauth" =>		if(argc != 4)			return (nil, "usage: Tauth afid uname aname");		return (ref Tmsg.Auth(0, fid(av[1]), av[2], av[3]), nil);	"Tflush" =>		if(argc != 2)			return (nil, "usage: Tflush oldtag");		return (ref Tmsg.Flush(0, tag(av[1])), nil);	"Tattach" =>		if(argc != 5)			return (nil, "usage: Tattach fid afid uname aname");		return (ref Tmsg.Attach(0, fid(av[1]), fid(av[2]), av[3], av[4]), nil);	"Twalk" =>		if(argc < 3)			return (nil, "usage: Twalk fid newfid [name...]");		names: array of string;		if(argc > 3)			names = av[3:];		return (ref Tmsg.Walk(0, fid(av[1]), fid(av[2]), names), nil);	"Topen" =>		if(argc != 3)			return (nil, "usage: Topen fid mode");		return (ref Tmsg.Open(0, fid(av[1]), atoi(av[2])), nil);	"Tcreate" =>		if(argc != 5)			return (nil, "usage: Tcreate fid name perm mode");		return (ref Tmsg.Create(0, fid(av[1]), av[2], atoi(av[3]), atoi(av[4])), nil);	"Tread" =>		if(argc != 4)			return (nil, "usage: Tread fid offset count");		return (ref Tmsg.Read(0, fid(av[1]), atob(av[2]), atoi(av[3])), nil);	"Twrite" =>		if(argc != 4)			return (nil, "usage: Twrite fid offset data");		return (ref Tmsg.Write(0, fid(av[1]), atob(av[2]), array of byte av[3]), nil);	"Tclunk" =>		if(argc != 2)			return (nil, "usage: Tclunk fid");		return (ref Tmsg.Clunk(0, fid(av[1])), nil);	"Tremove" =>		if(argc != 2)			return (nil, "usage: Tremove fid");		return (ref Tmsg.Remove(0, fid(av[1])), nil);	"Tstat" =>		if(argc != 2)			return (nil, "usage: Tstat fid");		return (ref Tmsg.Stat(0, fid(av[1])), nil);	"Twstat" =>		if(argc != 8)			return (nil, "usage: Twstat fid name uid gid mode mtime length");		return (ref Tmsg.Wstat(0, fid(av[1]), dir(av[2], av[3], av[4], val(av[5]), val(av[6]), bigval(av[7]))), nil);	"nexttag" =>		if(argc < 2)			return (nil, sys->sprint("next tag is %d", nexttag));		nexttag = tag(av[1]);		return (nil, nil);	"dump" =>		verbose++;		return (nil, nil);	* =>		return (nil, "unknown message type");	}} ## server side# Treader(fd: ref Sys->FD){	while((m := Tmsg.read(fd, msgsize)) != nil){		sys->print("<- %s\n", m.text());		if(tagof m == tagof Tmsg.Readerror)			quit(1);	}	sys->print("styxchat: clients hungup\n");} Rwriter(fd: ref Sys->FD){	in := bufio->fopen(stdin, Sys->OREAD);	while((l := in.gets('\n')) != nil){		if(l != nil && l[0] == '#')			continue;		(r, err) := Rparse(l);		if(r == nil){			if(err != nil)				sys->print("?%s\n", err);		}else{			a := r.pack();			if(a != nil){				sys->print("-> %s\n", r.text());				n := len a;				if(n <= msgsize){					if(sys->write(fd, a, len a) != len a)						sys->print("?write error to clients: %r\n");				}else					sys->print("?message bigger than agreed: %d bytes\n", n);			}else				sys->fprint(sys->fildes(2), "styxchat: R-message conversion failed\n");		}	}} qid(s: string): Sys->Qid{	(nf, flds) := sys->tokenize(s, ".");	q := Sys->Qid(big 0, 0, 0);	if(nf < 1)		return q;	q.path = atob(hd flds);	if(nf < 2)		return q;	q.vers = atoi(hd tl flds);	if(nf < 3)		return q;	q.qtype = mode(hd tl tl flds);	return q;} mode(s: string): int{	if(len s > 0 && s[0] >= '0' && s[0] <= '9')		return atoi(s);	mode := 0;	for(i := 0; i < len s; i++){		case s[i] {		'd' =>			mode |= Sys->QTDIR;		'a' =>			mode |= Sys->QTAPPEND;		'u' =>			mode |= Sys->QTAUTH;		'l' =>			mode |= Sys->QTEXCL;		'f' =>			;		* =>			sys->fprint(sys->fildes(2), "styxchat: unknown mode character %c, ignoring\n", s[i]);		}	}	return mode;} rdir(a: array of string): Sys->Dir{	d := sys->zerodir;	d.qid = qid(a[0]);	d.mode = atoi(a[1]) | (d.qid.qtype<<24);	d.atime = atoi(a[2]);	d.mtime = atoi(a[3]);	d.length = atob(a[4]);	d.name = a[5];	d.uid = a[6];	d.gid = a[7];	d.muid = a[8];	return d;} Rparse(s: string): (ref Rmsg, string){	args := str->unquoted(s);	if(args == nil)		return (nil, nil);	argc := len args;	av := array[argc] of string;	for(i:=0; args != nil; args = tl args)		av[i++] = hd args;	case av[0] {	"Rversion" =>		if(argc != 4)			return (nil, "usage: Rversion tag messagesize version");		return (ref Rmsg.Version(tag(av[1]), atoi(av[2]), av[3]), nil);	"Rauth" =>		if(argc != 3)			return (nil, "usage: Rauth tag aqid");		return (ref Rmsg.Auth(tag(av[1]), qid(av[2])), nil);	"Rflush" =>		if(argc != 2)			return (nil, "usage: Rflush tag");		return (ref Rmsg.Flush(tag(av[1])), nil);	"Rattach" =>		if(argc != 3)			return (nil, "usage: Rattach tag qid");		return (ref Rmsg.Attach(tag(av[1]), qid(av[2])), nil);	"Rwalk" =>		if(argc < 2)			return (nil, "usage: Rwalk tag [qid ...]");		qids := array[argc-2] of Sys->Qid;		for(i = 0; i < len qids; i++)			qids[i] = qid(av[i+2]);		return (ref Rmsg.Walk(tag(av[1]), qids), nil);	"Ropen" =>		if(argc != 4)			return (nil, "usage: Ropen tag qid iounit");		return (ref Rmsg.Open(tag(av[1]), qid(av[2]), atoi(av[3])), nil);	"Rcreate" =>

Lines 1–500 of 557.