ref: 5a9e919a0872f18fd39b62ab3c74216f0666e2e6
dir: /main.c/
#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); }