#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;
win->eventfd = awinfsopen(win, "event", ORDWR);
while(aeventnext(win, &ev)) {
switch(ev.type) {
case 'L':
case 'l':
aeventsend(win, &ev);
break;
default:
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) {
buf[n] = '\0'; filter(buf); /* null terminate and filter */
fprint(fd, "%s %s\n", prefix, buf);
};
/* 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 'n':
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 'x':
case 'X':
if(strcmp(ev.text, "Del") == 0)
goto quit;
if(strcmp(ev.text, "Send") == 0)
sendmessage(input);
if(strcmp(ev.text, "Act") == 0)
sendaction(input);
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);
}