mails/ 775 0 0 0 11256242430 77705ustar00nemosysmails/Arch 775 0 0 177 11256150113 10554ustar00nemosys#!/bin/rc # # Archive the message shown in the Acme window # where it runs, or messages named in std. input. exec Mvmesg a $* mails/Delmesg 775 0 0 176 11256150101 11253ustar00nemosys#!/bin/rc # # Delete the message shown in the Acme window # where it runs, or messages named in std. input. exec Mvmesg d $* mails/Mvmesg 775 0 0 2330 11256225143 11154ustar00nemosys#!/bin/rc # # Delete the message shown in the Acme window # where it runs, or messages named in std. input. rfork e if(~ $#* 0){ echo usage: Mvmesg char ... >[1=2] exit usage } chr=$1 ; shift w = /mnt/acme/$winid file = `{cat $w/tag | sed 's/ .*//'} switch($file){ case */text d=`{echo $file | sed 's,/text,,'} if (! test -d $d || ! ~ $d /mail/box/$user/* ) { echo $d is not a mail directory exit not } dd=`{basename -d $d} dn=`{basename $d} # touch "text" to let other programs know this file is new # e.g., let content search tools re-index the file for this new path mv $dd/$dn $dd/$chr.$dn && touch $dd/$chr.$dn/text >[2]/dev/null echo del >/mnt/acme/$winid/ctl case /mail/box/$user/* tee /tmp/mvmesg.$pid | \ sed 's,(.* )?(20[0-9][0-9][0-9][0-9])/([^0-9]?\.?)([0-9]*)/text.*,mv \2/\3\4 \2/'^$chr^'.\4,' | \ rc >[2]/dev/null cat /tmp/mvmesg.$pid | sed 'sx([0-9])/[a-z]?\.?([0-9])x\1/'^$chr^'.\2x' mv /tmp/mvmesg.$pid /tmp/mvmesg.$user # in case we want to undo... case * echo Delmesg in a strange place? } # plumb a mail event so mail list is updated # for those not running mailplumb. plumb -s plumb -d seemail -t text \ -a 'filetype=vwhois mailtype=delete sender=any' /mail/box/$user/msgs exit '' mails/Post 775 0 0 3716 11256232250 10651ustar00nemosys#!/bin/rc # # As Send, but on-line and from acme. # (from sape) rfork ne fn deliver { sed '1,/^$/d' $1 > body.$1 sed '/^$/q' $1 > hdr.$1 addrs=`{grep -i '^To: ' hdr.$1 | sed 's/^[Tt][Oo]: //'} ccs=`{grep -i '^Cc: ' hdr.$1 | sed 's/^[Cc][Cc]: //'} atts=`{grep -i '^Attach: ' hdr.$1 | sed 's/^[Aa]ttach: //' } incl=`{grep -i '^Include: ' hdr.$1 | sed 's/^[Ii]nclude: //' } repl=`{grep -i '^Replying: ' hdr.$1 | sed 's/^[Rr]eplying: //' } xaddrs=$addrs xccs=$ccs if (! ~ $#ccs 0) ccs=-C^$ccs if (! ~ $#atts 0) atts=-a^$atts if (! ~ $#incl 0) incl=-A^$incl if (! ~ $#repl 0){ if(! test -f $repl && test -f /mail/box/$user/msgs/$repl) repl=/mail/box/$user/msgs/$repl if(! test -f $repl){ arepl=`{echo $repl | sed 's,(.*)/([0-9]+)/raw,\1/a.\2/raw,'} if(test -f $arepl) repl=$arepl if(test -f /mail/box/$user/msgs/$arepl) repl=/mail/box/$user/msgs/$arepl } if(! test -f $repl){ echo orginal message not found >[1=2] exit orig } upas/fs -p -f $repl repl=-R/mail/fs/mbox/1 } done=ok { for(t in $xaddrs) echo 'To: '^$"t for(t in $xccs) echo 'Cc: '^$"t } > addrs.$1 { cat addrs.$1 ; grep -vi '^(To|Cc|Attach|Include|Replying): ' hdr.$1 ; cat body.$1 } | { if(upas/marshal $repl $atts $incl $ccs $addrs >[2] errs.$1){ rm errs.$1 hdr.$1 body.$1 addrs.$1 $1 } if not { done=failed echo spool: upas/marshal $repl $atts $incl $ccs $addrs cat errs.$1 cat errs.$1 | mail -s'returned mail' $user } } if (! ~ $#repl 0) unmount /mail/fs if(~ $done ok) status='' if not status='deliver failed' } cd /mail/box/$user/out || exit nombox w = /mnt/acme/$winid f = `{awk '{print $1}' $w/tag} if(! ~ $f /mail/box/$user/out/Out.*){ echo not an outgoing mail file to be posted exit nopostfile } echo put >$w/ctl if(! test -e $f){ echo noexist exit noexist } if(deliver `{basename $f}) echo del >$w/ctl if not{ if(test -e /mail/box/$user/log) echo `{date} Post delivered $f >>/mail/box/$user/log } exit '' mails/Reply 775 0 0 2100 11256227561 11012ustar00nemosys#!/bin/rc # # Reply to message shown in the Acme window # where it runs, or to the 1st mail named in # std. input or compose a new mail otherwise. rfork e w = /mnt/acme/$winid file = `{cat $w/tag | sed 's/ .*//'} fname=/mail/box/$user/out/Out.$pid if(! ~ $"file *text){ file=`{sed 1q | sed 's,(.* )?(20[0-9][0-9][0-9][0-9])/(.?\.?[0-9]*)/text.*,\2/\3/text,'} if(! ~ $"file *text){ file=new { echo 'To: ' echo 'Subject: ' echo } > $fname plumb $fname exit '' } if not file=`{pwd}^/^$"file } switch($file){ case /mail/*/text m=`{echo $file | sed 's,/text$,,'} upas/fs -p -f $m/raw rt=`{cat /mail/fs/mbox/1/replyto} f=`{cat /mail/fs/mbox/1/from} cc=`{cat /mail/fs/mbox/1/cc} if(! ~ $#cc 0) cc=`{echo $cc ; echo ; cat /mail/fs/mbox/1/to} if not cc=`{cat /mail/fs/mbox/1/to} { if (~ $#rt 0) echo To: $f if not echo To: $rt if (! ~ $#cc 0) echo Cc: $cc grep '^Subject: ' $m/text | sed 1q echo Replying: $m/raw echo q '> ' $m/text } > $fname unmount /mail/fs plumb $fname case * do not know how to reply to that. } exit '' mails/Spam 775 0 0 177 11256150130 10576ustar00nemosys#!/bin/rc # # Archive the message shown in the Acme window # where it runs, or messages named in std. input. exec Mvmesg s $* mails/guide 664 0 0 2725 11256233671 11025ustar00nemosys# # See mails(1). The convention is that # /mail/box/$user/msgs is the main mail # file system. Everything is kept within. # virtual folders are created by using text # files like /mail/box/$user/msgs/9fans # whose contents are similar to the main # window of Mails (or to the output of mails). # # Doing so, all relative paths for mails will # work within acme and most mail2fs scripts will # be happy. # There are helper programs, most of them work either # on a mail window or on the mails listed on the std input. # |Arch -> archive mails (move them to a.xxxx) # |Spam -> mark as spam (s.xxxx) # (all these scripts are really calls to Mvmesg) # |Delmesg -> delete them (d.xxxx) # >Reply -> reply to mail (selected) # # You may bind -a /acme/mails /bin # or copy and customize away these scripts. # cp src/[A-Z]* $home/bin/rc # open your inbox (/mail/box/$user/msgs) Mails # open ALL mails in your inbox (spam, deleted, ....) Mails -a # open ALL mails for 200908 Mails -a msgs 200908 # open a virtual folder for 9fans (/mail/box/$user/msgs/9fans) Mails 9fans # open # apply command |Spam to selected mails Edit , x/unwantedlogin/ +- |Spam # select everything and archive it :, |Arch # add 9fans and inferno mail to virtual mail box. # (you may use sort(1) to sort by date, sender, etc.) # this may be used to tag mails by adding them to # a per-tag text file. >grep '9fans|inferno' >> 9fans # On text files (other than the inbox) you may # cut/paste/... to remove/add/... mails mails/src/ 775 0 0 0 11256242430 105575ustar00nemosysmails/src/dat.h 664 0 0 3361 11255447556 11522ustar00nemosystypedef struct Event Event; typedef struct Mails Mails; typedef struct Window Window; enum { Stack = 8192, EVENTSIZE = 256, NEVENT = 5, /* mailbox types */ Mdir = 0, Mfile, }; struct Mails { char* path; char* month; int type; int edited; int warned; Window* win; void (*list)(Mails*); }; struct Event { int c1; int c2; int q0; int q1; int flag; int nb; int nr; char b[EVENTSIZE*UTFmax+1]; Rune r[EVENTSIZE+1]; }; struct Window { /* file descriptors */ int ctl; int event; int addr; int data; Biobuf* body; /* event input */ char buf[512]; char* bufp; int nbuf; Event e[NEVENT]; int id; int open; Channel*cevent; }; char* cleanpath(char* file, char* dir); void ctlprint(int fd, char *fmt, ...); void* emalloc(uint n); void* erealloc(void *p, uint n); void error(char *fmt, ...); char* esmprint(char *fmt, ...); char* estrdup(char *s); void main(int, char**); Window* newwindow(void); char* readfile(char *file, int *np); char* tcmdoutput(char* cmd, long *noutp); void winclean(Window *w); void winclosebody(Window *w); int windel(Window *w, int sure); void windormant(Window *w); void wineventproc(void *v); void wingetevent(Window *w, Event *e); void winname(Window *w, char *s); void winopenbody(Window *w, int mode); int winopenfile(Window *w, char *f); void winread(Window *w, uint q0, uint q1, char *data); char* winreadbody(Window *w, int *np); char* winselection(Window *w); int winsetaddr(Window *w, char *addr, int errok); void wintagwrite(Window *w, char *s, int n); void winwritebody(Window *w, char *s, int n); int winwritedata(Window *w, char *addr, char *s, int n); void winwriteevent(Window *w, Event *e); #pragma varargck argpos error 1 #pragma varargck argpos ctlprint 2 #pragma varargck argpos esmprint 1 ustar00nemosysmails/src/mails.c 664 0 0 17374 11256233507 12071ustar00nemosys/* * Adapted from Acme's Mail, mostly by * removing code and adding bits to process plan b * mailboxes. */ #include #include #include #include #include #include #include "dat.h" enum{ /* * Refresh list with a 5 minutes interval, * even if plumb events do not arrive. */ Refreshival = 5 * 60 }; #define dprint if(debug)fprint static char Mailscmds[] = "Get |Arch |Spam >Reply |Delmesg "; static int allflag; static Mails mails; static Channel* refreshc; static int plumbfd; static int debug; static int refreshival = Refreshival; static void usage(void) { fprint(2, "usage: Mails [-a] [-i secs] [mdir] [month]\n"); threadexitsall("usage"); } static int cmd(Mails *m, char *s) { Window *w; char *args[10]; int nargs; w = m->win; nargs = tokenize(s, args, nelem(args)); if(nargs == 0) return 0; if(strcmp(args[0], "Mails") == 0) return 1; else if(strcmp(s, "Put") == 0 || strcmp(s, "Get") == 0){ m->edited = 0; if(m->type == Mdir){ sendul(refreshc, 1); /* safety */ return 1; } }else if(strcmp(s, "Del") == 0){ windel(w, 1); threadexitsall(nil); } return 0; } /* * Keep longest common prefix and suffix of old and new * and replace the rest of old in window with new. * Acme uses runes; this function computes char infixes. * positions are converted later. Not too efficient. */ static void infixdiff(char *old, char *new, int *preflen, int *suflen) { int i, j; for(i = 0; old[i] != 0 && new[i] != 0 && old[i] == new[i]; i++) ; *preflen = i; i = strlen(old) - 1; j = strlen(new) - 1; for(*suflen = 0; i >= *preflen && j >= *preflen && old[i] == new[j]; ){ i--; j--; (*suflen)++; } } static void mergetext(Mails *m, char *old, char *new) { Window *w; int olen, nlen, plen, slen, p0, p1; char buf[50]; w = m->win; if(strcmp(old, new) == 0) return; olen = strlen(old); nlen = strlen(new); infixdiff(old, new, &plen, &slen); p0 = plen; if(plen > 0) p0 = utfnlen(old, plen); if(slen == plen) p1 = p0; else p1 = utfnlen(old, olen - slen); snprint(buf, sizeof(buf), "#%d,#%d", p0, p1); dprint(2, "changes: %s\n", buf); if(plen >= nlen - slen){ dprint(2, "text1 []\n"); winwritedata(w, buf, "", 0); }else{ dprint(2, "text2 [%*s]\n", nlen-slen+1, new+plen); winwritedata(w, buf, new+plen, nlen-slen+1); } winclean(w); windormant(w); } /* * Updating mail lists keeps the longest prefix and suffix * that did not change and replaces everything else. * This works well enough without depending on the contents * of the window. */ static void mdirlist(Mails *mails) { char *body; int nbody; char *new; long nnew; char *cmd; char *f; body = winreadbody(mails->win, &nbody); if(body == nil) error("%s: read window body failed", mails->path); if(allflag) f = "-a "; else f = ""; if(mails->month != nil) cmd = esmprint("/bin/mails %s%s %s", f, mails->path, mails->month); else cmd = esmprint("/bin/mails %s%s", f, mails->path); new = tcmdoutput(cmd, &nnew); mergetext(mails, body, new); free(body); free(cmd); free(new); } static void mfilelist(Mails *mails) { char* body; int nbody; char *file; int nfile; body = winreadbody(mails->win, &nbody); if(body == nil) error("%s: read window body failed", mails->path); file = readfile(mails->path, &nfile); if(file == nil) error("%s: readfile: %r", mails->path); mergetext(mails, body, file); free(body); free(file); } static void edited(Mails *m) { if(m->edited == 0){ m->edited = 1; if(m->warned == 0){ fprint(2, "%s will not be updated while dirty,\n" "you may use Put/Get to make it clean.\n", m->path); m->warned = 1; } } } static void mainctl(void *v) { Mails *m; Window *w; Event *e, *e2, *eq, *ea; int na; char *s, *t, *buf; m = v; w = m->win; proccreate(wineventproc, w, Stack); for(;;){ e = recvp(w->cevent); switch(e->c1){ default: Unknown: print("unknown message %c%c\n", e->c1, e->c2); break; case 'E': /* write to body; can't affect us */ break; case 'F': /* generated by our actions; ignore */ break; case 'K': /* type away; we don't care (mostly) */ if(e->c2 == 'D' || e->c2 == 'I') edited(m); break; case 'M': switch(e->c2){ case 'x': case 'X': ea = nil; e2 = nil; if(e->flag & 2) e2 = recvp(w->cevent); if(e->flag & 8){ ea = recvp(w->cevent); na = ea->nb; recvp(w->cevent); }else na = 0; s = e->b; if((e->flag&2) && e->nb==0) s = e2->b; if(na){ t = emalloc(strlen(s)+1+na+1); sprint(t, "%s %s", s, ea->b); s = t; } if(!cmd(m, s)) winwriteevent(w, e); if(na) free(s); break; case 'l': case 'L': buf = nil; eq = e; if(e->flag & 2){ e2 = recvp(w->cevent); eq = e2; } s = eq->b; if(eq->q1>eq->q0 && eq->nb==0){ buf = emalloc((eq->q1-eq->q0)*UTFmax+1); winread(w, eq->q0, eq->q1, buf); s = buf; } USED(s); /* processing on looks goes here, if needed */ if(1) winwriteevent(w, e); free(buf); break; case 'I': /* modify away; we don't care */ case 'D': edited(m); case 'd': case 'i': break; default: goto Unknown; } } } } static void mkmailswin(Mails *mails) { char *dirname; mails->win = newwindow(); if(mails->type == Mdir) dirname = esmprint("%s/list", mails->path); else dirname = nil; winname(mails->win, dirname ? dirname : mails->path); free(dirname); wintagwrite(mails->win, Mailscmds, strlen(Mailscmds)); threadcreate(mainctl, mails, Stack); } /* * Refresh the entire mail list upon plumb events, * otherwise, changes made by hand on the file system * may go unnoticed. Crude but effective. */ static void refreshthread(void*) { threadsetname("refreshthread"); while(recvul(refreshc) != 0){ while(nbrecvul(refreshc) != 0) ; if(mails.edited == 0) mails.list(&mails); } threadexits(nil); } static void plumbreadproc(void*) { Plumbmsg *m; threadsetname("plumbreadproc"); for(;;){ m = plumbrecv(plumbfd); if(m == nil) threadexits(nil); plumbfree(m); sendul(refreshc, 1); } } static void timerproc(void*) { threadsetname("timerproc"); for(;;){ sendul(refreshc, 1); sleep(refreshival * 1000); } } static void initmails(char *dir, char *month) { char *top; char *dflt; char *s; Dir *d; mails.path = mails.month = nil; if(month != nil) mails.month = estrdup(month); top = esmprint("/mail/box/%s", getuser()); dflt = esmprint("%s/msgs", top); if(dir == nil) dir = dflt; if(access(dir, AEXIST) == 0) dir = estrdup(dir); else{ s = cleanpath(dir, dflt); if(access(s, AEXIST) == 0) dir = s; else{ free(s); s = cleanpath(dir, top); if(access(s, AEXIST) == 0) dir = s; else sysfatal("%s: %r", dir); } } mails.path = dir; d = dirstat(mails.path); if(d == nil) sysfatal("%s: %r", mails.path); if(d->qid.type&QTDIR){ mails.type = Mdir; mails.list = mdirlist; }else{ mails.type = Mfile; mails.list = mfilelist; } free(top); free(dflt); free(d); } void threadmain(int argc, char *argv[]) { char *month; char *dir; month = dir = nil; ARGBEGIN{ case 'd': debug = 1; break; case 'a': allflag = 1; break; case 'i': refreshival = atoi(EARGF(usage())); if(refreshival < 5) refreshival = 5; break; default: usage(); }ARGEND switch(argc){ case 0: dir = nil; break; case 1: dir = argv[0]; break; case 2: dir = argv[0]; month = argv[1]; default: usage(); } initmails(dir, month); mkmailswin(&mails); refreshc = chancreate(sizeof(ulong), 5); if(refreshc == nil) sysfatal("chancreate: %r\n"); if(refreshival != 0) proccreate(timerproc, nil, Stack); plumbfd = plumbopen("seemail", OREAD|OCEXEC); if(plumbfd >= 0) proccreate(plumbreadproc, nil, Stack); refreshthread(nil); threadexits(nil); } ited; int warned; Window* win; void (*list)(Mails*); }; struct Event { int c1; int c2; int q0; int q1; int flag; int nb; int nr; char b[EVENTSIZE*UTFmax+1]; Rune r[EVENTSIZE+1]; }; struct Window { /* file descriptors */ int ctl; int event; imails/src/mkfile 664 0 0 503 11255020714 11725ustar00nemosyssyms 8c -aa mesg.c reply.c util.c win.c >>syms mails/src/tcmd.c 664 0 0 2005 11255020753 11650ustar00nemosys#include #include #include #include #include "dat.h" typedef struct Arg Arg; struct Arg { int p[2]; char**argv; }; enum { Nargs = 64 }; static void cmdproc(void* x) { Arg* a = x; char** argv; argv = a->argv; close(a->p[0]); dup(a->p[1], 1); close(a->p[1]); procexec(nil, argv[0], argv); threadexits(nil); } char* tcmdoutput(char* cmd, long *noutp) { long tot, n; Arg a; char* argv[Nargs]; int argc; char* s; char* out; long sz; *noutp = -1; s = estrdup(cmd); argc = tokenize(s, argv, nelem(argv)-1); if(argc < 0) error("tcmdoutput: no args"); argv[argc] = nil; if (pipe(a.p) < 0) error("no pipes"); a.argv = argv; procrfork(cmdproc, &a, 8*1024, RFFDG|RFENVG); close(a.p[1]); sz = 16*1024; out = emalloc(sz); for(tot = 0; sz - tot > 1 ; tot +=n){ n = read(a.p[0], out+tot, sz - tot); if (n <= 0) break; if(tot + n == sz){ sz += 16*1024; out = erealloc(out, sz); } } free(s); close(a.p[0]); out[tot] = 0; *noutp = tot; return out; } freshc; static int plumbfd; static int debug; static int refreshival = Refreshival; static void usage(void) { fprint(2, "usage: Mails [-a] [-i secs] [mdir] [month]\n"); threadexitsall("usage"); } static int cmd(Mails *m, char *s) { Window *w; char *args[10]; int nargs; w = m->win; nargs = tokenize(s, args, nelem(args)); if(nargs == 0) return 0; if(strcmp(args[0], "Mails") == 0) return 1; else if(strcmp(s, "Put") == 0 || strcmp(s, "Get") == 0){ m->edited = 0; if(m->type == Mdir){ mails/src/util.c 664 0 0 3630 11255447030 11704ustar00nemosys#include #include #include #include #include #include "dat.h" void* emalloc(uint n) { void *p; p = malloc(n); if(p == nil) error("can't malloc: %r"); memset(p, 0, n); setmalloctag(p, getcallerpc(&n)); return p; } void* erealloc(void *p, uint n) { p = realloc(p, n); if(p == nil) error("can't realloc: %r"); setmalloctag(p, getcallerpc(&n)); return p; } char* estrdup(char *s) { char *t; t = emalloc(strlen(s)+1); strcpy(t, s); return t; } char* esmprint(char *fmt, ...) { va_list args; char *s; va_start(args, fmt); s = vsmprint(fmt, args); va_end(args); if(s == nil) sysfatal("no memory"); return s; } void error(char *fmt, ...) { Fmt f; char buf[64]; va_list arg; fmtfdinit(&f, 2, buf, sizeof buf); fmtprint(&f, "%s: ", argv0); va_start(arg, fmt); fmtvprint(&f, fmt, arg); va_end(arg); fmtprint(&f, "\n"); fmtfdflush(&f); threadexitsall(fmt); } void ctlprint(int fd, char *fmt, ...) { int n; va_list arg; va_start(arg, fmt); n = vfprint(fd, fmt, arg); va_end(arg); if(n <= 0) error("control file write error: %r"); } char* readfile(char *file, int *np) { char *data; int fd, len; Dir *d; if(np != nil) *np = 0; fd = open(file, OREAD); if(fd < 0) return nil; d = dirfstat(fd); len = 0; if(d != nil) len = d->length; free(d); data = emalloc(len+1); read(fd, data, len); close(fd); if(np != nil) *np = len; return data; } char* cleanpath(char* file, char* dir) { char* s; char* t; char cwd[512]; assert(file && file[0]); if(file[1]) file = estrdup(file); else { s = file; file = emalloc(3); file[0] = s[0]; file[1] = 0; } s = cleanname(file); if(s[0] != '/' && access(s, AEXIST) == 0){ getwd(cwd, sizeof(cwd)); t = esmprint("%s/%s", cwd, s); free(s); return t; } if(s[0] != '/' && dir != nil && dir[0] != 0){ t = esmprint("%s/%s", dir, s); free(s); s = cleanname(t); } return s; } ed(Mails *m) { if(m->edited == 0){ m->edited = 1; if(m->warned == 0){ fprint(2, "%s will not be mails/src/win.c 664 0 0 12477 11256210273 11553ustar00nemosys#include #include #include #include #include #include "dat.h" Window* newwindow(void) { char buf[12]; Window *w; w = emalloc(sizeof(Window)); w->ctl = open("/mnt/wsys/new/ctl", ORDWR|OCEXEC); if(w->ctl<0 || read(w->ctl, buf, 12)!=12) error("can't open window ctl file: %r"); ctlprint(w->ctl, "noscroll\n"); w->id = atoi(buf); w->event = winopenfile(w, "event"); w->addr = -1; /* will be opened when needed */ w->body = nil; w->data = -1; w->cevent = chancreate(sizeof(Event*), 0); return w; } void wineventproc(void *v) { Window *w; int i; w = v; for(i=0; ; i++){ if(i >= NEVENT) i = 0; wingetevent(w, &w->e[i]); sendp(w->cevent, &w->e[i]); } } static int winopenfile1(Window *w, char *f, int m) { char buf[64]; int fd; sprint(buf, "/mnt/wsys/%d/%s", w->id, f); fd = open(buf, m|OCEXEC); if(fd < 0) error("can't open window file %s: %r", f); return fd; } int winopenfile(Window *w, char *f) { return winopenfile1(w, f, ORDWR); } void wintagwrite(Window *w, char *s, int n) { int fd; fd = winopenfile(w, "tag"); if(write(fd, s, n) != n) error("tag write: %r"); close(fd); } void winname(Window *w, char *s) { ctlprint(w->ctl, "name %s\n", s); } void winopenbody(Window *w, int mode) { char buf[256]; sprint(buf, "/mnt/wsys/%d/body", w->id); w->body = Bopen(buf, mode|OCEXEC); if(w->body == nil) error("can't open window body file: %r"); } void winclosebody(Window *w) { if(w->body != nil){ Bterm(w->body); w->body = nil; } } void winwritebody(Window *w, char *s, int n) { if(w->body == nil) winopenbody(w, OWRITE); if(Bwrite(w->body, s, n) != n) error("write error to window: %r"); } static int wingetec(Window *w) { if(w->nbuf == 0){ w->nbuf = read(w->event, w->buf, sizeof w->buf); if(w->nbuf <= 0){ /* probably because window has exited, and only called by wineventproc, so just shut down */ threadexits(nil); } w->bufp = w->buf; } w->nbuf--; return *w->bufp++; } static int wingeten(Window *w) { int n, c; n = 0; while('0'<=(c=wingetec(w)) && c<='9') n = n*10+(c-'0'); if(c != ' ') error("event number syntax"); return n; } static int wingeter(Window *w, char *buf, int *nb) { Rune r; int n; r = wingetec(w); buf[0] = r; n = 1; if(r >= Runeself) { while(!fullrune(buf, n)) buf[n++] = wingetec(w); chartorune(&r, buf); } *nb = n; return r; } void wingetevent(Window *w, Event *e) { int i, nb; e->c1 = wingetec(w); e->c2 = wingetec(w); e->q0 = wingeten(w); e->q1 = wingeten(w); e->flag = wingeten(w); e->nr = wingeten(w); if(e->nr > EVENTSIZE) error("event string too long"); e->nb = 0; for(i=0; inr; i++){ e->r[i] = wingeter(w, e->b+e->nb, &nb); e->nb += nb; } e->r[e->nr] = 0; e->b[e->nb] = 0; if(wingetec(w) != '\n') error("event syntax error"); } void winwriteevent(Window *w, Event *e) { fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); } void winread(Window *w, uint q0, uint q1, char *data) { int m, n, nr; char buf[256]; if(w->addr < 0) w->addr = winopenfile(w, "addr"); if(w->data < 0) w->data = winopenfile(w, "data"); m = q0; while(m < q1){ n = sprint(buf, "#%d", m); if(write(w->addr, buf, n) != n) error("error writing addr: %r"); n = read(w->data, buf, sizeof buf); if(n <= 0) error("reading data: %r"); nr = utfnlen(buf, n); while(m+nr >q1){ do; while(n>0 && (buf[--n]&0xC0)==0x80); --nr; } if(n == 0) break; memmove(data, buf, n); data += n; *data = 0; m += nr; } } void windormant(Window *w) { if(w->addr >= 0){ close(w->addr); w->addr = -1; } if(w->body != nil){ Bterm(w->body); w->body = nil; } if(w->data >= 0){ close(w->data); w->data = -1; } } int windel(Window *w, int sure) { if(sure) write(w->ctl, "delete\n", 7); else if(write(w->ctl, "del\n", 4) != 4) return 0; /* event proc will die due to read error from event file */ windormant(w); close(w->ctl); w->ctl = -1; close(w->event); w->event = -1; return 1; } void winclean(Window *w) { if(w->body) Bflush(w->body); ctlprint(w->ctl, "clean\n"); } int winsetaddr(Window *w, char *addr, int errok) { if(w->addr < 0) w->addr = winopenfile(w, "addr"); if(write(w->addr, addr, strlen(addr)) < 0){ if(!errok) error("error writing addr(%s): %r", addr); return 0; } return 1; } int winwritedata(Window *w, char *addr, char *s, int n) { int r; r = -1; if(addr != nil) if(winsetaddr(w, addr, 1)) ctlprint(w->ctl, "dot=addr\n"); if(w->data < 0) w->data = winopenfile(w, "data"); if(w->data >= 0) r = write(w->data, s, n); close(w->data); w->data = -1; return r; } /* can't use readfile because acme doesn't report the length */ char* winreadbody(Window *w, int *np) { char *s; int m, na, n; if(w->body != nil) winclosebody(w); winopenbody(w, OREAD); s = nil; na = 0; n = 0; for(;;){ if(na < n+512){ na += 1024; s = realloc(s, na+1); } m = Bread(w->body, s+n, na-n); if(m <= 0) break; n += m; } s[n] = 0; winclosebody(w); *np = n; return s; } char* winselection(Window *w) { int fd, m, n; char *buf; char tmp[256]; fd = winopenfile1(w, "rdsel", OREAD); if(fd < 0) error("can't open rdsel: %r"); n = 0; buf = nil; for(;;){ m = read(fd, tmp, sizeof tmp); if(m <= 0) break; buf = erealloc(buf, n+m+1); memmove(buf+n, tmp, m); n += m; buf[n] = '\0'; } close(fd); return buf; }