~foura/wired

5a9e919a0872f18fd39b62ab3c74216f0666e2e6 — james palmer 11 months ago 1abb973
rewired. toggleable grep window soon™
1 files changed, 258 insertions(+), 156 deletions(-)

M main.c
M main.c => main.c +258 -156
@@ 1,242 1,344 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <acme.h>

typedef struct Session Session;
struct Session {
	AWin *cwin;
	AWin *iwin;
	
typedef struct Server Server;
typedef struct Chan Chan;
typedef struct Ctl Ctl;

struct Server {
	char *addr;
	char *mtpt;
	char *chan;
	char *usr;
	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;
};

enum { Stack = 4*1024, };
struct Ctl {
	int type;
	void *aux;
};

void
chatcatproc(void *aux)
enum {
	MExit,
	MMessage,
	MSend,
	MAct,

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

int
sendctl(Channel *chan, int type, void *aux)
{
	Session *s;
	AWin *w;
	Ctl ctl;
	
	w = aux;
	s = w->aux;
	ctl.type = type;
	ctl.aux = aux;
	
	awincat(w, s->fd);
	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
wineventproc(void *aux)
eventproc(void *aux)
{
	AWin *w;
	AWin *win;
	AEvent ev;
	Session *s;
	Channel *ctl;
	Channel *eventc;
	
	w = aux;
	s = w->aux;
	ctl = s->ctl;
	eventc = aeventlisten(w);
	
	while(recv(eventc, &ev)) {
		switch(ev.type) {
		case 'x':
			if(strcmp(ev.text, "Del") == 0)
				sendp(ctl, "quit");
			if(strcmp(ev.text, "Send") == 0)
				sendp(ctl, "send");
			if(strcmp(ev.text, "Action") == 0)
				sendp(ctl, "act");
				
			break;
		case 'I':
			if(strchr(ev.text, '') != 0)
				sendp(ctl, "send");

			break;
		case 'l':
		case 'L':
			aeventsend(w, &ev);
		}
	}	
	threadsetname("event");
		
	win = aux;
	win->eventfd = awinfsopen(win, "event", ORDWR);
	
	while(aeventnext(win, &ev) > 0)
		handleevent(win->aux, win, &ev);

	threadexits(nil);
}

char *
readbody(AWin *w)
void
logproc(void *aux)
{
	Biobuf *fd;
	int addr;
	char *buf;
	int i;
	
	sleep(10);
	
	addr = awinfsopen(w, "addr", OWRITE);
	fd = awinfsbopen(w, "data", OREAD);
	Chan *chan;
	Ctl ctl;
	AWin *win;

	fprint(addr, ",");
	buf = Brdstr(fd, '\0', 0);
	threadsetname("log");
	
	if(!buf)
		return nil;
	chan = aux;
	win = awincreate();
	fprint(win->ctlfd, "name /wired/%s/log\n"
		"scratch\n" "nomenu\n", chan->name);
	win->aux = chan;
	
	for(i = strlen(buf)-1; i >= 0; i--) {
		if(buf[i] == '' || buf[i] == '\n')
			buf[i] = '\0';
		else
	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;
	
	if(buf[0] == '\0') {
		awinclear(w);
		return nil;
	}
	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;
	
	awinclear(w);
	Bterm(fd);
	close(addr);
	return buf;
	fprint(chan->fd, "%s %s\n", prefix, message);
	free(prefix);
	free(message);
}

void
domessage(Session *s, char *msg)
inputproc(void *aux)
{
	char *buf;
	Server *srv;
	Chan *chan;
	Ctl ctl;
	AWin *win;

	threadsetname("input");
	
	if(!msg)
		return;
	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;
	
	buf = smprint("%s • %s\n", s->usr, msg);
	write(s->fd, buf, strlen(buf));
	proccreate(eventproc, win, Stack);
	
	free(buf);
	free(msg);
	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
doaction(Session *s, char *msg)
readproc(void *aux)
{
	Chan *chan;
	char *buf;
	long n;
	Ctl ctl;

	threadsetname("read");
	
	if(!msg)
		return;
	chan = aux;
	buf = mallocz(Buf, 1);
	
	buf = smprint("* %s %s\n", s->usr, msg);
	write(s->fd, buf, strlen(buf));
	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);
	free(msg);
	chanfree(chan->readc);
	threadexits(nil);
}

void
dochat(char *mtpt, char *chan, char *usr)
connectchan(Server *srv, char *name)
{
	Session *s;
	char buf[128];
	char *ctl;
	
	s = mallocz(sizeof(Session), 1);

	s->mtpt = strdup(mtpt);
	s->chan = strdup(chan);
	s->usr = strdup(usr);
	
	snprint(buf, sizeof(buf), "%s/%s", s->mtpt, s->chan);
	s->fd = open(buf, ORDWR);
	if(s->fd < 0) {
		free(s->mtpt);
		free(s->chan);
		free(s->usr);
		free(s);
		
		sysfatal("couldn't read channel: %r");
	};
		
	s->cwin = awincreate();
	awinctl(s->cwin, "name /wired/chat\n");
	awinctl(s->cwin, "scratch\n");
	awinctl(s->cwin, "nomenu\n");
	s->cwin->aux = s;
	
	s->iwin = awincreate();
	awinctl(s->iwin, "name /wired/input\n");
	awinctl(s->iwin, "scratch\n");
	awinctl(s->iwin, "nomenu\n");
	awinsettag(s->iwin, " Send Action ");
	s->iwin->aux = s;
	
	s->ctl = chancreate(sizeof(char*), 0);
	
	proccreate(chatcatproc, s->cwin, Stack);
	proccreate(wineventproc, s->cwin, Stack);
	proccreate(wineventproc, s->iwin, Stack);
	
	while(ctl = recvp(s->ctl)) {
		if(strcmp(ctl, "send") == 0)
			domessage(s, readbody(s->iwin));
		if(strcmp(ctl, "act") == 0)
			doaction(s, readbody(s->iwin));
		if(strcmp(ctl, "quit") == 0)
	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
dialchat(char *srv, char *mtpt)
connectsrv(Server *srv)
{
	int fd;
	
	if((fd = dial(srv, nil, nil, nil)) < 0)
	if((fd = dial(srv->addr, nil, nil, nil)) < 0)
		sysfatal("couldn't dial chat server: %r");
	if(mount(fd, -1, mtpt, MREPL, "") == -1)
	if(mount(fd, -1, srv->mtpt, MREPL, "") < 0)
		sysfatal("couldn't mount chat server: %r");
}

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

void
threadmain(int argc, char *argv[])
{
	char *srv, *mtpt, *user, *chan;
	Server *srv;
	char *channel;
	
	srv = "tcp!chat.9p.zone!9990";
	mtpt = "/n/chat";
	chan = "chat";
	user = getuser();
	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 = EARGF(usage());
		srv->addr = EARGF(usage());
		break;
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 'c':
		chan = EARGF(usage());
		srv->mtpt = EARGF(usage());
		break;
	case 'u':
		user = EARGF(usage());
		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
	
	dialchat(srv, mtpt);
	dochat(mtpt, chan, user);
	
	awincloseall();
	threadexitsall(nil);
	connectsrv(srv);
	connectchan(srv, channel);
	unmount(nil, srv->mtpt);

	free(srv);
	threadexits(nil);
}