shithub: wired

ref: 1b8752f7adc9f4faa6210eba9d914a2a44e5f1db
dir: /main.c/

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

char *srv = "tcp!9p.zone!9990";
char *mtpt = "/n/wired";
char *chan = "chat";
char *sep = "•";
char *nick;

void
connectsrv(void)
{
	int fd;

	fd = dial(srv, nil, nil, nil);
	if(fd < 0)
		sysfatal("dial: %r");
	if(mount(fd, -1, mtpt, MREPL, "") < 0)
		sysfatal("mount: %r");
}

AWin *
makewin(char *name, Channel *c)
{
	AWin *win;

	win = awincreate();
	win->aux = c;

	fprint(win->ctlfd, "name /wired/%s/%s\n", chan, name);
	fprint(win->ctlfd, "scratch\nnomenu\n");

	return win;
}

void
readproc(void *aux)
{
	AWin *win;
	char *file;
	char buf[8192];
	int fd;
	long n;

	threadsetname("read");

	win = aux;
	file = smprint("%s/%s", mtpt, chan);
	if(!file) {
		awincloseall();
		sysfatal("smprint failed");
	}

	fd = open(file, OREAD);
	if(fd < 0) {
		awincloseall();
		sysfatal("channel not found");
	}

	while((n = read(fd, buf, sizeof(buf))) > 0)
		write(win->bodyfd, buf, n);

	awincloseall();
	sysfatal("failed to read channel");
}

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

	threadsetname("event");

	win = aux;
	events = win->aux;
	aeventopen(win);

	while(aeventread(win, &ev))
		send(events, &ev);

	threadexits(nil);
}

void
filter(char *s)
{
	int len;
	int i, j;

	/* strip eot */
	j = 0; len = strlen(s);
	for(i = 0; i < len; i++) {
		if(s[i] != '') {
			s[j] = s[i];
			j++;
		}
	}
	s[j] = 0;
	
	/* strip trailing nl */
	len = strlen(s);
	for(i = len-1; i > 0; i--)
		if(s[i] == '\n')
			s[i] = 0;
		else
			break;
}

void
writemessage(char *prefix, AWin *win)
{
	int fd;
	char *file;
	char buf[4096];
	long n;

	file = smprint("%s/%s", mtpt, chan);
	if(!file) {
		awincloseall();
		sysfatal("smprint failed");
	}

	fd = open(file, OWRITE);
	if(fd < 0) {
		awincloseall();
		sysfatal("couldn't write channel");
	}

	/* read the input window in 4096 byte chunks.
	 * if the message is too large it will be split awkwardly :(
	 * hopefully nobody needs a message that large */
	fprint(win->addrfd, ",");
	while((n = read(win->datafd, buf, sizeof(buf)-1)) > 0) {

		/* null terminate and filter */
		buf[n] = '\0'; 
		filter(buf); 

		/* no blank messages */
		if(buf[0] == 0)
			return;

		fprint(fd, "%s %s\n", prefix, buf);
	};
	close(fd);


	/* clear: use of buf here means nothing, the write is 0 bytes.
	 * it is only there to prevent a crash due to a bad syscall. */
	fprint(win->addrfd, ",");
	write(win->datafd, buf, 0);
}

void
sendmessage(AWin *win)
{
	char *prefix;

	prefix = smprint("%s %s", nick, sep);
	if(!prefix) {
		awincloseall();
		sysfatal("smprint failed");
	}

	writemessage(prefix, win);
	free(prefix);
}

void
sendaction(AWin *win)
{
	char *prefix;
	
	prefix = smprint("* %s", nick);
	if(!prefix) {
		awincloseall();
		sysfatal("smprint failed");
	}
	
	writemessage(prefix, win);
	free(prefix);
}

void
usage(void)
{
	fprint(2, "usage: %s [-s srv] [-n nick] [channel...]", argv0);
	threadexitsall(nil);
}

void
threadmain(int argc, char *argv[])
{
	AWin *log, *input;
	Channel *events;
	AEvent ev;

	nick = getuser();

	ARGBEGIN {
	case 'u':
		nick = EARGF(usage());
		break;
	case 's':
		srv = EARGF(usage());
		break;
	case 'c':
		chan = EARGF(usage());
		break;
	} ARGEND

	connectsrv();
	events = chancreate(sizeof(AEvent), 0);

	log = makewin("log", events);
	input = makewin("input", events);
	
	awinsettag(input, " Send Act ");

	proccreate(readproc, log, 16*1024);
	proccreate(eventproc, log, 8*1024);
	proccreate(eventproc, input, 8*1024);

	while(recv(events, &ev)) {
		switch(ev.type) {
		case 'l':
		case 'L':
			aeventwrite(ev.win, &ev);
			break;

		case 'x':
		case 'X':
			if(strcmp(ev.text, "Del") == 0)
				goto quit;
			else if(strcmp(ev.text, "Send") == 0)
				sendmessage(input);
			else if(strcmp(ev.text, "Act") == 0)
				sendaction(input);
			else
				aeventwrite(ev.win, &ev);

			break;
		case 'I':
			/* there is an EOT char here, it allows ctrl+d to send */
			if(strchr(ev.text, ''))
				sendmessage(input);
		}
	}

quit:
	unmount(nil, mtpt);
	awincloseall();
	threadexitsall(nil);
}