shithub: wired

ref: 5a9e919a0872f18fd39b62ab3c74216f0666e2e6
dir: /main.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <acme.h>

typedef struct Server Server;
typedef struct Chan Chan;
typedef struct Ctl Ctl;

struct Server {
	char *addr;
	char *mtpt;
	char *user;
	
	char *sep;
	char *actsep;

	Chan *channels;
};

struct Chan {
	char *name;
	
	Channel *logc;
	Channel *inputc;
	Channel *readc;
	
	Channel *ctl;
	
	int logproc;
	int inputproc;
	int readproc;

	int fd;

	Server *srv;
};

struct Ctl {
	int type;
	void *aux;
};

enum {
	MExit,
	MMessage,
	MSend,
	MAct,

	Stack = 4*1024,
	Buf = 8*1024,
};

int
sendctl(Channel *chan, int type, void *aux)
{
	Ctl ctl;
	
	ctl.type = type;
	ctl.aux = aux;
	
	return send(chan, &ctl);
}

void
handleevent(Chan *chan, AWin *win, AEvent *ev)
{
	switch(ev->type) {
	case 'x':
	case 'X':
		if(strcmp(ev->text, "Del") == 0)
			sendctl(chan->ctl, MExit, nil);
		else if(strcmp(ev->text, "Send") == 0)
			sendctl(chan->inputc, MSend, nil);
		else if(strcmp(ev->text, "Action") == 0)
			sendctl(chan->inputc, MAct, nil);
		else
			aeventsend(win, ev);
			
		break;
	case 'I':
		if(strchr(ev->text, '') != 0)
			sendctl(chan->ctl, MSend, nil);
			
		break;
	case 'l':
	case 'L':
		aeventsend(win, ev);
		break;
	}
}

void
eventproc(void *aux)
{
	AWin *win;
	AEvent ev;

	threadsetname("event");
		
	win = aux;
	win->eventfd = awinfsopen(win, "event", ORDWR);
	
	while(aeventnext(win, &ev) > 0)
		handleevent(win->aux, win, &ev);

	threadexits(nil);
}

void
logproc(void *aux)
{
	Chan *chan;
	Ctl ctl;
	AWin *win;

	threadsetname("log");
	
	chan = aux;
	win = awincreate();
	fprint(win->ctlfd, "name /wired/%s/log\n"
		"scratch\n" "nomenu\n", chan->name);
	win->aux = chan;
	
	proccreate(eventproc, win, Stack);
	
	while(recv(chan->logc, &ctl)) {
		switch(ctl.type) {
		case MMessage:
			fprint(win->bodyfd, "%s", ctl.aux);
			free(ctl.aux);
			break;
		case MExit:
			goto exit;
		}
	}

exit:
	close(win->eventfd);
	chanfree(chan->logc);
	awinclose(win);
	threadexits(nil);
}

void
sendmessage(AWin *win, char *fmt, ...)
{
	Chan *chan;
	char *prefix;
	char *message;
	va_list args;
	
	chan = win->aux;

	va_start(args, fmt);
	prefix = vsmprint(fmt, args);
	va_end(args);
	
	message = mallocz(Buf, 1);
	fprint(win->addrfd, ",");
	if(read(win->datafd, message, Buf-1) < 0)
		return;
	
	fprint(chan->fd, "%s %s\n", prefix, message);
	free(prefix);
	free(message);
}

void
inputproc(void *aux)
{
	Server *srv;
	Chan *chan;
	Ctl ctl;
	AWin *win;

	threadsetname("input");
	
	chan = aux;
	srv = chan->srv;
	win = awincreate();
	fprint(win->ctlfd, "name /wired/%s/input\n"
		"scratch\n" "nomenu\n", chan->name);
	awinsettag(win, " Send Action ");
	win->aux = chan;
	
	proccreate(eventproc, win, Stack);
	
	while(recv(chan->inputc, &ctl)) {
		switch(ctl.type) {
		case MSend:
			sendmessage(win, "%s %s", srv->user, srv->sep);
			break;
		case MAct:
			sendmessage(win, "%s %s", srv->actsep, srv->user);
			break;
		case MExit:
			goto exit;
		}
	}

exit:
	close(win->eventfd);
	chanfree(chan->inputc);
	awinclose(win);
	threadexits(nil);
}

void
readproc(void *aux)
{
	Chan *chan;
	char *buf;
	long n;
	Ctl ctl;

	threadsetname("read");
	
	chan = aux;
	buf = mallocz(Buf, 1);
	
	while((n = read(chan->fd, buf, Buf-1)) > 0) {
		buf[n] = '\0';
		sendctl(chan->logc, MMessage, strdup(buf));
		
		if(nbrecv(chan->readc, &ctl))
			if(ctl.type == MExit)
				break;
	}
	
	free(buf);
	chanfree(chan->readc);
	threadexits(nil);
}

void
connectchan(Server *srv, char *name)
{
	Chan *chan;
	char *buf;
	Ctl ctl;
	
	chan = mallocz(sizeof(Chan), 1);
	chan->name = name;
	chan->srv = srv;
	
	buf = smprint("%s/%s", srv->mtpt, name);
	chan->fd = open(buf, ORDWR);
	if(chan->fd < 0) {
		fprint(2, "channel %s not found\n", name);
		free(chan);
		return;
	}
	
	chan->logc = chancreate(sizeof(Ctl), 0);
	chan->inputc = chancreate(sizeof(Ctl), 0);
	chan->readc = chancreate(sizeof(Ctl), 0);
	
	chan->ctl = chancreate(sizeof(Ctl), 0);
	
	chan->logproc = proccreate(logproc, chan, Stack);
	chan->inputproc = proccreate(inputproc, chan, Stack);
	chan->readproc = proccreate(readproc, chan, Stack);
	
	while(recv(chan->ctl, &ctl)) {
		if(ctl.type == MExit) {
			threadint(chan->readproc); /* try to interrupt the read */
			sendctl(chan->logc, MExit, nil);
			sendctl(chan->inputc, MExit, nil);
			break;
		}
	}


	chanfree(chan->ctl);
	free(chan);
	free(buf);
	return;
}

void
connectsrv(Server *srv)
{
	int fd;
	
	if((fd = dial(srv->addr, nil, nil, nil)) < 0)
		sysfatal("couldn't dial chat server: %r");
	if(mount(fd, -1, srv->mtpt, MREPL, "") < 0)
		sysfatal("couldn't mount chat server: %r");
}

void
usage(void)
{
	fprint(2, "usage: %s [-s srv] [-m mtpt] [-u user] "
		"[-S sep] [-A actsep]\n", argv0);
	threadexitsall("usage");
}

void
threadmain(int argc, char *argv[])
{
	Server *srv;
	char *channel;
	
	srv = mallocz(sizeof(Server), 1);
	srv->addr = "tcp!9p.zone!9990";
	srv->mtpt = "/n/wired";
	srv->sep = "•";
	srv->actsep = "*";
	srv->user = getuser();

	channel = "chat";
	
	ARGBEGIN {
	case 's':
		srv->addr = EARGF(usage());
		break;
	case 'm':
		srv->mtpt = EARGF(usage());
		break;
	case 'u':
		srv->user = EARGF(usage());
		break;
	case 'S':
		srv->sep = EARGF(usage());
		break;
	case 'A':
		srv->actsep = EARGF(usage());
		break;
	case 'c':
		channel = EARGF(usage());
		break;
	default:
		usage();
	} ARGEND
	
	connectsrv(srv);
	connectchan(srv, channel);
	unmount(nil, srv->mtpt);

	free(srv);
	threadexits(nil);
}