* [9fans] tiny patches for cmds on the bitsy
@ 2001-05-03 10:37 nemo
0 siblings, 0 replies; 2+ messages in thread
From: nemo @ 2001-05-03 10:37 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 1064 bytes --]
Hi,
some silly changes I made:
The first one changes acme Mail to include the option `-S' to:
- avoid showing message parts before the message is shown
- use a shorter summary for mails (not too much screen space on the ipaq)
It also changes Mail to avoid reading body files for mails before the
information is really needed. Together, these changes allow me to use
acme on the ipaq to read my mail w/o having to read all the
attachments.
The first one also includes the `-s' option, which is like `-S', but
does not try so hard to make the summary fit on one line of the bitsy
display. I'm using `-s' on the laptop and `-S' on the bitsy.
The second diff changes stats to show the bitsy battery too. The
/dev/battery file is different and has a different format. What I do is
to open /dev/battery if the regular /mnt/apm/battery is not found. This
is a kludge AFAIK; I think that /mnt/apm/battery and /dev/battery
should be actually the same file, using the same format. But for now,
this makes stats work.
[-- Attachment #2: diff --]
[-- Type: text/plain, Size: 3336 bytes --]
/acme/mail/src/dat.h:56 a /usr/nemo/src/9/sys/src/cmd/acmemail/dat.h:57,58
> uchar recursed;
> uchar level;
/acme/mail/src/dat.h:138 a /usr/nemo/src/9/sys/src/cmd/acmemail/dat.h:141
> extern int shortmenu;
Only in /usr/nemo/src/9/sys/src/cmd/acmemail: diff
Only in /usr/nemo/src/9/sys/src/cmd/acmemail: guide
/acme/mail/src/mail.c:29 a /usr/nemo/src/9/sys/src/cmd/acmemail/mail.c:30,31
> int shortmenu;
>
/acme/mail/src/mail.c:33 c /usr/nemo/src/9/sys/src/cmd/acmemail/mail.c:35
< threadprint(2, "usage: Mail [mailboxname [directoryname]]\n");
---
> threadprint(2, "usage: Mail [-sS] [mailboxname [directoryname]]\n");
/acme/mail/src/mail.c:47 a /usr/nemo/src/9/sys/src/cmd/acmemail/mail.c:50
>
/acme/mail/src/mail.c:59 a /usr/nemo/src/9/sys/src/cmd/acmemail/mail.c:63
> shortmenu = 0;
/acme/mail/src/mail.c:60 a /usr/nemo/src/9/sys/src/cmd/acmemail/mail.c:65,72
> case 's':
> shortmenu = 1;
> break;
> case 'S':
> shortmenu = 2;
> break;
> default:
> usage();
/acme/mail/src/mail.c:118 a /usr/nemo/src/9/sys/src/cmd/acmemail/mail.c:131
> mbox.level= 0;
/acme/mail/src/mesg.c:161 a /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:162
> mbox->recursed = 1;
/acme/mail/src/mesg.c:180 a /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:182,183
> m->level= mbox->level+1;
> m->recursed = 0;
/acme/mail/src/mesg.c:195 c /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:198,202
< readmbox(m, dir, m->name);
---
>
> if (m->level != 1){
> m->recursed = 1;
> readmbox(m, dir, m->name);
> }
/acme/mail/src/mesg.c:269 a /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:277,296
> if (ind == 0 && shortmenu) {
> char fmt[80];
> char s[80];
> int len;
> int lens;
>
> len = (shortmenu > 1) ? 10 : 30;
> lens = (shortmenu > 1) ? 25 : 30;
> if (ind == 0 && m->subject[0] == '\0'){
> snprint(fmt, 80, " %%-%d.%ds", len, len);
> snprint(s, 80, fmt, m->fromcolon);
> }else{
> snprint(fmt, 80, " %%-%d.%ds %%-%d.%ds", len, len, lens, lens);
> snprint(s, 80, fmt, m->fromcolon, m->subject);
> }
> i = estrdup(s);
>
> return i;
> }
>
/acme/mail/src/mesg.c:289 c /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:316
< mesgmenu0(Window *w, Message *mbox, char *realdir, char *dir, int ind, Biobuf *fd, int onlyone)
---
> mesgmenu0(Window *w, Message *mbox, char *realdir, char *dir, int ind, Biobuf *fd, int onlyone, int dotail)
/acme/mail/src/mesg.c:309,310 c /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:336,337
< if(m->tail)
< mesgmenu0(w, m, realdir, name, ind+1, fd, 0);
---
> if(dotail && m->tail)
> mesgmenu0(w, m, realdir, name, ind+1, fd, 0, dotail);
/acme/mail/src/mesg.c:325 c /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:352
< mesgmenu0(w, mbox, mbox->name, "", 0, w->body, 0);
---
> mesgmenu0(w, mbox, mbox->name, "", 0, w->body, 0, !shortmenu);
/acme/mail/src/mesg.c:339 c /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:366
< mesgmenu0(w, mbox, mbox->name, "", 0, b, 1);
---
> mesgmenu0(w, mbox, mbox->name, "", 0, b, 1, !shortmenu);
/acme/mail/src/mesg.c:491 d /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:517
<
/acme/mail/src/mesg.c:812 a /usr/nemo/src/9/sys/src/cmd/acmemail/mesg.c:839,842
> if(m->level == 1 && m->recursed == 0){
> m->recursed = 1;
> readmbox(m, rootdir, m->name);
> }
[-- Attachment #3: diff-stats --]
[-- Type: text/plain, Size: 1132 bytes --]
/sys/src/cmd/stats.c:48 c stats.c:48
< /* /mnt/apm/battery */
---
> /* /mnt/apm/battery | /dev/battery */
/sys/src/cmd/stats.c:59 a stats.c:60
> int bitsybatfd;
/sys/src/cmd/stats.c:576,577 c stats.c:577,586
< if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
< memmove(m->batterystats, a, sizeof(m->batterystats));
---
> m->bitsybatfd = -1;
> if (m->batteryfd >= 0){
> if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
> memmove(m->batterystats, a, sizeof(m->batterystats));
> }else{
> snprint(buf, sizeof buf, "%s/dev/battery", mpt);
> m->bitsybatfd = open(buf, OREAD);
> if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
> memmove(m->batterystats, a, sizeof(m->batterystats));
> }
/sys/src/cmd/stats.c:654 a stats.c:664,665
> if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
> memmove(m->batterystats, a, sizeof(m->batterystats));
/sys/src/cmd/stats.c:783 c stats.c:794,797
< *vmax = 100;
---
> if (m->bitsybatfd >= 0)
> *vmax = 184; // at least on my bitsy...
> else
> *vmax = 100;
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [9fans] tiny patches for cmds on the bitsy
@ 2001-05-03 13:24 nemo
0 siblings, 0 replies; 2+ messages in thread
From: nemo @ 2001-05-03 13:24 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 122 bytes --]
Probably more people in 9fans also hate applying diffs,
So here you have the full files w/ the diffs applied.
hth
[-- Attachment #2: Type: message/rfc822, Size: 784 bytes --]
From: "rob pike" <rob@plan9.bell-labs.com>
To: nemo@gsyc.escet.urjc.es
Subject: Re: [9fans] tiny patches for cmds on the bitsy
Date: Thu, 3 May 2001 09:16:31 -0400
Message-ID: <200105031316.PAA14066@gsyc.escet.urjc.es>
can you just send me the source files? i hate applying diffs.
thanks.
-rob
[-- Attachment #3: stats.c --]
[-- Type: text/plain, Size: 26326 bytes --]
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <auth.h>
#include <fcall.h>
#include <draw.h>
#include <event.h>
#define MAXNUM 8 /* maximum number of numbers on data line */
typedef struct Graph Graph;
typedef struct Machine Machine;
struct Graph
{
int colindex;
Rectangle r;
int *data;
int ndata;
char *label;
void (*newvalue)(Machine*, long*, long*, int);
void (*update)(Graph*, long, long);
Machine *mach;
int overflow;
Image *overtmp;
};
enum
{
/* /dev/swap */
Mem = 0,
Maxmem,
Swap,
Maxswap,
/* /dev/sysstats */
Procno = 0,
Context,
Interrupt,
Syscall,
Fault,
TLBfault,
TLBpurge,
Load,
/* /net/ether0/0/stats */
In = 0,
Out,
Err0,
/* /mnt/apm/battery | /dev/battery */
Battery,
};
struct Machine
{
char *name;
int remote;
int statsfd;
int swapfd;
int etherfd;
int batteryfd;
int bitsybatfd;
int disable;
long devswap[4];
long devsysstat[8];
long prevsysstat[8];
int nproc;
long netetherstats[8];
long prevetherstats[8];
long batterystats[2];
char buf[1024];
char *bufp;
char *ebufp;
};
enum
{
Mainproc,
Mouseproc,
NPROC,
};
enum
{
Ncolor = 6,
Ysqueeze = 2, /* vertical squeezing of label text */
Labspace = 2, /* room around label */
Dot = 2, /* height of dot */
Opwid = 5, /* strlen("add ") or strlen("drop ") */
Nlab = 3, /* max number of labels on y axis */
Lablen = 16, /* max length of label */
Lx = 4, /* label tick length */
};
enum Menu2
{
Mcontext,
Mether,
Methererr,
Metherin,
Metherout,
Mfault,
Mintr,
Mload,
Mmem,
Mswap,
Msyscall,
Mtlbmiss,
Mtlbpurge,
Mbattery,
Nmenu2,
};
char *menu2str[Nmenu2+1] = {
"add context ",
"add ether ",
"add ethererr",
"add etherin ",
"add etherout",
"add fault ",
"add intr ",
"add load ",
"add mem ",
"add swap ",
"add syscall ",
"add tlbmiss ",
"add tlbpurge",
"add battery ",
nil,
};
void contextval(Machine*, long*, long*, int),
etherval(Machine*, long*, long*, int),
ethererrval(Machine*, long*, long*, int),
etherinval(Machine*, long*, long*, int),
etheroutval(Machine*, long*, long*, int),
faultval(Machine*, long*, long*, int),
intrval(Machine*, long*, long*, int),
loadval(Machine*, long*, long*, int),
memval(Machine*, long*, long*, int),
swapval(Machine*, long*, long*, int),
syscallval(Machine*, long*, long*, int),
tlbmissval(Machine*, long*, long*, int),
tlbpurgeval(Machine*, long*, long*, int),
batteryval(Machine*, long*, long*, int);
Menu menu2 = {menu2str, nil};
int present[Nmenu2];
void (*newvaluefn[Nmenu2])(Machine*, long*, long*, int init) = {
contextval,
etherval,
ethererrval,
etherinval,
etheroutval,
faultval,
intrval,
loadval,
memval,
swapval,
syscallval,
tlbmissval,
tlbpurgeval,
batteryval,
};
Image *cols[Ncolor][3];
Graph *graph;
Machine *mach;
Font *mediumfont;
char *mysysname;
char argchars[] = "bceEfimlnpstw";
int pids[NPROC];
int parity; /* toggled to avoid patterns in textured background */
int nmach;
int ngraph; /* totaly number is ngraph*nmach */
double scale = 1.0;
int logscale = 0;
int ylabels = 0;
char *procnames[NPROC] = {"main", "mouse"};
void
killall(char *s)
{
int i, pid;
pid = getpid();
for(i=0; i<NPROC; i++)
if(pids[i] && pids[i]!=pid)
postnote(PNPROC, pids[i], "kill");
exits(s);
}
void*
emalloc(ulong sz)
{
void *v;
v = malloc(sz);
if(v == nil) {
fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
killall("mem");
}
memset(v, 0, sz);
return v;
}
void*
erealloc(void *v, ulong sz)
{
v = realloc(v, sz);
if(v == nil) {
fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
killall("mem");
}
return v;
}
char*
estrdup(char *s)
{
char *t;
if((t = strdup(s)) == nil) {
fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
killall("mem");
}
return t;
}
void
mkcol(int i, int c0, int c1, int c2)
{
cols[i][0] = allocimagemix(display, c0, DWhite);
cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
}
void
colinit(void)
{
mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
if(mediumfont == nil)
mediumfont = font;
/* Peach */
mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
/* Aqua */
mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
/* Yellow */
mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
/* Green */
mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
/* Blue */
mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
/* Grey */
cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
}
int
loadbuf(Machine *m, int *fd)
{
int n;
if(*fd < 0)
return 0;
seek(*fd, 0, 0);
n = read(*fd, m->buf, sizeof m->buf);
if(n <= 0){
close(*fd);
*fd = -1;
return 0;
}
m->bufp = m->buf;
m->ebufp = m->buf+n;
return 1;
}
void
label(Point p, int dy, char *text)
{
char *s;
Rune r[2];
int w, maxw, maxy;
p.x += Labspace;
maxy = p.y+dy;
maxw = 0;
r[1] = '\0';
for(s=text; *s; ){
if(p.y+mediumfont->height-Ysqueeze > maxy)
break;
w = chartorune(r, s);
s += w;
w = runestringwidth(mediumfont, r);
if(w > maxw)
maxw = w;
runestring(screen, p, display->black, ZP, mediumfont, r);
p.y += mediumfont->height-Ysqueeze;
}
}
Point
paritypt(int x)
{
return Pt(x+parity, 0);
}
Point
datapoint(Graph *g, int x, long v, long vmax)
{
Point p;
double y;
p.x = x;
y = ((double)v)/(vmax*scale);
if(logscale){
/*
* Arrange scale to cover a factor of 1000.
* vmax corresponds to the 100 mark.
* 10*vmax is the top of the scale.
*/
if(y <= 0.)
y = 0;
else{
y = log10(y);
/* 1 now corresponds to the top; -2 to the bottom; rescale */
y = (y+2.)/3.;
}
}
p.y = g->r.max.y - Dy(g->r)*y - Dot;
if(p.y < g->r.min.y)
p.y = g->r.min.y;
if(p.y > g->r.max.y-Dot)
p.y = g->r.max.y-Dot;
return p;
}
void
drawdatum(Graph *g, int x, long prev, long v, long vmax)
{
int c;
Point p, q;
c = g->colindex;
p = datapoint(g, x, v, vmax);
q = datapoint(g, x, prev, vmax);
if(p.y < q.y){
draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
}else{
draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
}
}
void
redraw(Graph *g, int vmax)
{
int i, c;
c = g->colindex;
draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
for(i=1; i<Dx(g->r); i++)
drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
g->overflow = 0;
}
void
update1(Graph *g, long v, long vmax)
{
char buf[32];
int overflow;
if(g->overflow && g->overtmp!=nil)
draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
g->data[0] = v;
g->overflow = 0;
if(logscale)
overflow = (v>10*vmax*scale);
else
overflow = (v>vmax*scale);
if(overflow && g->overtmp!=nil){
g->overflow = 1;
draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
sprint(buf, "%ld", v);
string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf);
}
}
/* read one line of text from buffer and process integers */
int
readnums(Machine *m, int n, long *a, int spanlines)
{
int i;
char *p, *ep;
if(spanlines)
ep = m->ebufp;
else
for(ep=m->bufp; ep<m->ebufp; ep++)
if(*ep == '\n')
break;
p = m->bufp;
for(i=0; i<n && p<ep; i++){
while(p<ep && !isdigit(*p))
p++;
if(p == ep)
break;
a[i] = strtol(p, &p, 10);
}
if(ep < m->ebufp)
ep++;
m->bufp = ep;
return i == n;
}
/* Network on fd1, mount driver on fd0 */
static int
filter(int fd)
{
int p[2];
if(pipe(p) < 0){
fprint(2, "stats: can't pipe: %r\n");
killall("pipe");
}
switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
case -1:
sysfatal("rfork record module");
case 0:
dup(fd, 1);
close(fd);
dup(p[0], 0);
close(p[0]);
close(p[1]);
execl("/bin/aux/fcall", "fcall", 0);
fprint(2, "stats: can't exec fcall: %r\n");
killall("fcall");
default:
close(fd);
close(p[0]);
}
return p[1];
}
/*
* 9fs
*/
int
connect9fs(char *addr)
{
char dir[4*NAMELEN], *na;
int fd;
fprint(2, "connect9fs...");
na = netmkaddr(addr, 0, "9fs");
fprint(2, "dial %s...", na);
if((fd = dial(na, 0, dir, 0)) < 0)
return -1;
fprint(2, "dir %s...", dir);
if(strstr(dir, "tcp"))
fd = filter(fd);
return fd;
}
/*
* exportfs
*/
int
connectexportfs(char *addr)
{
char buf[ERRLEN], dir[4*NAMELEN], *na;
int fd, n;
char *tree;
tree = "/";
na = netmkaddr(addr, 0, "exportfs");
if((fd = dial(na, 0, dir, 0)) < 0)
return -1;
if(auth(fd) < 0){
close(fd);
return -1;
}
n = write(fd, tree, strlen(tree));
if(n < 0){
close(fd);
return -1;
}
strcpy(buf, "can't read tree");
n = read(fd, buf, sizeof buf - 1);
if(n!=2 || buf[0]!='O' || buf[1]!='K'){
buf[sizeof buf - 1] = '\0';
werrstr("bad remote tree: %s\n", buf);
close(fd);
return -1;
}
if(strstr(dir, "tcp"))
fd = filter(fd);
return fd;
}
void
initmach(Machine *m, char *name)
{
int n, fd;
long a[MAXNUM];
char *p, mpt[256], buf[256];
p = strchr(name, '!');
if(p){
p++;
m->name = estrdup(p+1);
}else
p = name;
m->name = estrdup(p);
m->remote = (strcmp(p, mysysname) != 0);
if(m->remote == 0)
strcpy(mpt, "");
else{
snprint(mpt, sizeof mpt, "/n/%s", p);
fd = connectexportfs(name);
if(fd < 0){
fprint(2, "can't connect to %s: %r\n", name);
killall("connect");
}
if(mount(fd, mpt, MREPL, "") < 0){
fprint(2, "stats: mount %s on %s failed (%r); trying /n/sid\n", name, mpt);
strcpy(mpt, "/n/sid");
if(mount(fd, mpt, MREPL, "") < 0){
fprint(2, "stats: mount %s on %s failed: %r\n", name, mpt);
killall("mount");
}
}
}
snprint(buf, sizeof buf, "%s/dev/swap", mpt);
m->swapfd = open(buf, OREAD);
if(loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0))
memmove(m->devswap, a, sizeof m->devswap);
else
m->devswap[Maxmem] = m->devswap[Maxswap] = 100;
snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
m->statsfd = open(buf, OREAD);
if(loadbuf(m, &m->statsfd)){
for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
;
m->nproc = n;
}else
m->nproc = 1;
snprint(buf, sizeof buf, "%s/net/ether0/0/stats", mpt);
m->etherfd = open(buf, OREAD);
if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
memmove(m->netetherstats, a, sizeof m->netetherstats);
snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
m->batteryfd = open(buf, OREAD);
m->bitsybatfd = -1;
if (m->batteryfd >= 0){
if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
}else{
snprint(buf, sizeof buf, "%s/dev/battery", mpt);
m->bitsybatfd = open(buf, OREAD);
if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
}
}
jmp_buf catchalarm;
void
alarmed(void *a, char *s)
{
if(strcmp(s, "alarm") == 0)
notejmp(a, catchalarm, 1);
noted(NDFLT);
}
int
needswap(int init)
{
return init | present[Mmem] | present[Mswap];
}
int
needstat(int init)
{
return init | present[Mcontext] | present[Mfault] | present[Mintr] | present[Mload] |
present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
}
int
needether(int init)
{
return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
}
int
needbattery(int init)
{
return init | present[Mbattery];
}
void
readmach(Machine *m, int init)
{
int n, i;
long a[8];
char buf[32];
if(m->remote && (m->disable || setjmp(catchalarm))){
if(m->disable == 0){
snprint(buf, sizeof buf, "%s(dead)", m->name);
m->name = estrdup(buf);
if(display != nil) /* else we're still initializing */
eresized(0);
}
m->disable = 1;
memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
return;
}
if(m->remote){
notify(alarmed);
alarm(5000);
}
if(needswap(init) && loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0))
memmove(m->devswap, a, sizeof m->devswap);
if(needstat(init) && loadbuf(m, &m->statsfd)){
memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
memset(m->devsysstat, 0, sizeof m->devsysstat);
for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
for(i=0; i<nelem(m->devsysstat); i++)
m->devsysstat[i] += a[i];
}
if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
memmove(m->netetherstats, a, sizeof m->netetherstats);
}
if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
if(m->remote){
alarm(0);
notify(nil);
}
}
void
memval(Machine *m, long *v, long *vmax, int)
{
*v = m->devswap[Mem];
*vmax = m->devswap[Maxmem];
}
void
swapval(Machine *m, long *v, long *vmax, int)
{
*v = m->devswap[Swap];
*vmax = m->devswap[Maxswap];
}
void
contextval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[Context]-m->prevsysstat[Context];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
intrval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[Interrupt]-m->prevsysstat[Interrupt];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
syscallval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[Syscall]-m->prevsysstat[Syscall];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
faultval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[Fault]-m->prevsysstat[Fault];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
tlbmissval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[TLBfault]-m->prevsysstat[TLBfault];
*vmax = 10*m->nproc;
if(init)
*vmax = 10;
}
void
tlbpurgeval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge];
*vmax = 10*m->nproc;
if(init)
*vmax = 10;
}
void
loadval(Machine *m, long *v, long *vmax, int init)
{
*v = m->devsysstat[Load];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
etherval(Machine *m, long *v, long *vmax, int init)
{
*v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
etherinval(Machine *m, long *v, long *vmax, int init)
{
*v = m->netetherstats[In]-m->prevetherstats[In];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
etheroutval(Machine *m, long *v, long *vmax, int init)
{
*v = m->netetherstats[Out]-m->prevetherstats[Out];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
ethererrval(Machine *m, long *v, long *vmax, int init)
{
int i;
*v = 0;
for(i=Err0; i<nelem(m->netetherstats); i++)
*v += m->netetherstats[i];
*vmax = 10*m->nproc;
if(init)
*vmax = 10;
}
void
batteryval(Machine *m, long *v, long *vmax, int)
{
*v = m->batterystats[0];
if (m->bitsybatfd >= 0)
*vmax = 184; // at least on my bitsy...
else
*vmax = 100;
}
void
usage(void)
{
fprint(2, "usage: stats [-S scale] [-LY] [-%s] [machine...]\n", argchars);
exits("usage");
}
void
addgraph(int n)
{
Graph *g, *ograph;
int i, j;
static int nadd;
if(n > nelem(menu2str))
abort();
/* avoid two adjacent graphs of same color */
if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
nadd++;
ograph = graph;
graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
for(i=0; i<nmach; i++)
for(j=0; j<ngraph; j++)
graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
free(ograph);
ngraph++;
for(i=0; i<nmach; i++){
g = &graph[i*ngraph+(ngraph-1)];
memset(g, 0, sizeof(Graph));
g->label = menu2str[n]+Opwid;
g->newvalue = newvaluefn[n];
g->update = update1; /* no other update functions yet */
g->mach = &mach[i];
g->colindex = nadd%Ncolor;
}
present[n] = 1;
nadd++;
}
void
dropgraph(int which)
{
Graph *ograph;
int i, j, n;
if(which > nelem(menu2str))
abort();
/* convert n to index in graph table */
n = -1;
for(i=0; i<ngraph; i++)
if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
n = i;
break;
}
if(n < 0){
fprint(2, "stats: internal error can't drop graph\n");
killall("error");
}
ograph = graph;
graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
for(i=0; i<nmach; i++){
for(j=0; j<n; j++)
graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
free(ograph[i*ngraph+j].data);
freeimage(ograph[i*ngraph+j].overtmp);
for(j++; j<ngraph; j++)
graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
}
free(ograph);
ngraph--;
present[which] = 0;
}
void
addmachine(char *name)
{
if(ngraph > 0){
fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
usage();
}
if(mach == nil)
nmach = 0; /* a little dance to get us started with local machine by default */
mach = erealloc(mach, (nmach+1)*sizeof(Machine));
memset(mach+nmach, 0, sizeof(Machine));
initmach(mach+nmach, name);
nmach++;
}
void
labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
{
int j;
long v, vmax;
g->newvalue(g->mach, &v, &vmax, 1);
if(logscale){
for(j=1; j<=2; j++)
sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
*np = 2;
}else{
for(j=1; j<=3; j++)
sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
*np = 3;
}
}
int
labelwidth(void)
{
int i, j, n, w, maxw;
char strs[Nlab][Lablen];
maxw = 0;
for(i=0; i<ngraph; i++){
/* choose value for rightmost graph */
labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
for(j=0; j<n; j++){
w = stringwidth(mediumfont, strs[j]);
if(w > maxw)
maxw = w;
}
}
return maxw;
}
void
resize(void)
{
int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
Graph *g;
Rectangle machr, r;
long v, vmax;
char buf[128], labs[Nlab][Lablen];
draw(screen, screen->r, display->white, nil, ZP);
/* label left edge */
x = screen->r.min.x;
y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
dy = (screen->r.max.y - y)/ngraph;
dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
startx = x+dx+1;
starty = y;
for(i=0; i<ngraph; i++,y+=dy){
draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
label(Pt(x, y), dy, graph[i].label);
draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
}
/* label top edge */
dx = (screen->r.max.x - startx)/nmach;
for(x=startx, i=0; i<nmach; i++,x+=dx){
draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
j = dx/stringwidth(mediumfont, "0");
n = mach[i].nproc;
if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
j -= 3+(n>10)+(n>100);
if(j <= 0)
j = 1;
snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
}else
snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf);
}
maxx = screen->r.max.x;
/* label right, if requested */
if(ylabels && dy>Nlab*(mediumfont->height+1)){
wid = labelwidth();
if(wid < (maxx-startx)-30){
/* else there's not enough room */
maxx -= 1+Lx+wid;
draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
y = starty;
for(j=0; j<ngraph; j++, y+=dy){
/* choose value for rightmost graph */
g = &graph[ngraph*(nmach-1)+j];
labelstrs(g, labs, &nlab);
r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
if(j == ngraph-1)
r.max.y = screen->r.max.y;
draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
for(k=0; k<nlab; k++){
ly = y + (dy*(nlab-k)/(nlab+1));
draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
ly -= mediumfont->height/2;
string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]);
}
}
}
}
/* create graphs */
for(i=0; i<nmach; i++){
machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
if(i < nmach-1)
machr.max.x = startx+(i+1)*dx - 1;
y = starty;
for(j=0; j<ngraph; j++, y+=dy){
g = &graph[i*ngraph+j];
/* allocate data */
ondata = g->ndata;
g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
g->data = erealloc(g->data, g->ndata*sizeof(long));
if(g->ndata > ondata)
memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(long));
/* set geometry */
g->r = machr;
g->r.min.y = y;
g->r.max.y = y+dy - 1;
if(j == ngraph-1)
g->r.max.y = screen->r.max.y;
draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
g->overflow = 0;
r = g->r;
r.max.y = r.min.y+mediumfont->height;
r.max.x = r.min.x+stringwidth(mediumfont, "9999999");
freeimage(g->overtmp);
g->overtmp = nil;
if(r.max.x <= g->r.max.x)
g->overtmp = allocimage(display, r, screen->chan, 0, -1);
g->newvalue(g->mach, &v, &vmax, 0);
redraw(g, vmax);
}
}
flushimage(display, 1);
}
void
eresized(int new)
{
lockdisplay(display);
if(new && getwindow(display, Refnone) < 0) {
fprint(2, "stats: can't reattach to window\n");
killall("reattach");
}
resize();
unlockdisplay(display);
}
void
mouseproc(void)
{
Mouse mouse;
int i;
for(;;){
mouse = emouse();
if(mouse.buttons == 4){
lockdisplay(display);
for(i=0; i<Nmenu2; i++)
if(present[i])
memmove(menu2str[i], "drop ", Opwid);
else
memmove(menu2str[i], "add ", Opwid);
i = emenuhit(3, &mouse, &menu2);
if(i >= 0){
if(!present[i])
addgraph(i);
else if(ngraph > 1)
dropgraph(i);
resize();
}
unlockdisplay(display);
}
}
}
void
startproc(void (*f)(void), int index)
{
int pid;
switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
case -1:
fprint(2, "stats: fork failed: %r\n");
killall("fork failed");
case 0:
f();
fprint(2, "stats: %s process exits\n", procnames[index]);
if(index >= 0)
killall("process died");
exits(nil);
}
if(index >= 0)
pids[index] = pid;
}
void
main(int argc, char *argv[])
{
int i, j;
char *s;
long v, vmax, nargs;
char args[100];
nmach = 1;
mysysname = getenv("sysname");
if(mysysname == nil){
fprint(2, "stats: can't find $sysname: %r\n");
exits("sysname");
}
mysysname = estrdup(mysysname);
nargs = 0;
ARGBEGIN{
case 'S':
s = ARGF();
if(s == nil)
usage();
scale = atof(s);
if(scale <= 0.)
usage();
break;
case 'L':
logscale++;
break;
case 'Y':
ylabels++;
break;
default:
if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
usage();
args[nargs++] = ARGC();
}ARGEND
if(argc == 0){
mach = emalloc(nmach*sizeof(Machine));
initmach(&mach[0], mysysname);
readmach(&mach[0], 1);
}else{
for(i=0; i<argc; i++){
addmachine(argv[i]);
readmach(&mach[i], 1);
}
}
for(i=0; i<nargs; i++)
switch(args[i]){
default:
fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
usage();
case 'b':
addgraph(Mbattery);
break;
case 'c':
addgraph(Mcontext);
break;
case 'e':
addgraph(Mether);
break;
case 'E':
addgraph(Metherin);
addgraph(Metherout);
break;
case 'f':
addgraph(Mfault);
break;
case 'i':
addgraph(Mintr);
break;
case 'l':
addgraph(Mload);
break;
case 'm':
addgraph(Mmem);
break;
case 'n':
addgraph(Metherin);
addgraph(Metherout);
addgraph(Methererr);
break;
case 'p':
addgraph(Mtlbpurge);
break;
case 's':
addgraph(Msyscall);
break;
case 't':
addgraph(Mtlbmiss);
addgraph(Mtlbpurge);
break;
case 'w':
addgraph(Mswap);
break;
}
if(ngraph == 0)
addgraph(Mload);
for(i=0; i<nmach; i++)
for(j=0; j<ngraph; j++)
graph[i*ngraph+j].mach = &mach[i];
if(initdraw(nil, nil, "stats") < 0){
fprint(2, "stats: initdraw failed: %r\n");
exits("initdraw");
}
colinit();
einit(Emouse);
notify(nil);
startproc(mouseproc, Mouseproc);
pids[Mainproc] = getpid();
display->locking = 1; /* tell library we're using the display lock */
resize();
unlockdisplay(display); /* display is still locked from initdraw() */
for(;;){
for(i=0; i<nmach; i++)
readmach(&mach[i], 0);
lockdisplay(display);
parity = 1-parity;
for(i=0; i<nmach*ngraph; i++){
graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
graph[i].update(&graph[i], v, vmax);
}
flushimage(display, 1);
unlockdisplay(display);
sleep(1000);
}
}
[-- Attachment #4: dat.h --]
[-- Type: text/plain, Size: 3470 bytes --]
typedef struct Event Event;
typedef struct Message Message;
typedef struct Window Window;
enum
{
STACK = 8192,
EVENTSIZE = 256,
NEVENT = 5,
};
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;
};
struct Message
{
Window *w;
int ctlfd;
char *name;
char *replyname;
uchar opened;
uchar dirty;
uchar isreply;
uchar deleted;
uchar writebackdel;
uchar tagposted;
uchar recursed;
uchar level;
/* header info */
char *fromcolon; /* from header file; all rest are from info file */
char *from;
char *to;
char *cc;
char *replyto;
char *date;
char *subject;
char *type;
char *disposition;
char *filename;
char *digest;
Message *next; /* next in this mailbox */
Message *prev; /* prev in this mailbox */
Message *head; /* first subpart */
Message *tail; /* last subpart */
};
extern Window* newwindow(void);
extern int winopenfile(Window*, char*);
extern void winopenbody(Window*, int);
extern void winclosebody(Window*);
extern void wintagwrite(Window*, char*, int);
extern void winname(Window*, char*);
extern void winwriteevent(Window*, Event*);
extern void winread(Window*, uint, uint, char*);
extern int windel(Window*, int);
extern void wingetevent(Window*, Event*);
extern void wineventproc(void*);
extern void winwritebody(Window*, char*, int);
extern void winclean(Window*);
extern int winselect(Window*, char*, int);
extern int winsetaddr(Window*, char*, int);
extern char* winreadbody(Window*, int*);
extern void windormant(Window*);
extern void winsetdump(Window*, char*, char*);
extern void readmbox(Message*, char*, char*);
extern void rewritembox(Window*, Message*);
extern void mkreply(Message*, char*, char*);
extern void delreply(Message*);
extern int mesgadd(Message*, char*, Dir*, char*);
extern void mesgmenu(Window*, Message*);
extern void mesgmenunew(Window*, Message*);
extern int mesgopen(Message*, char*, char*, Message*, int, char*);
extern void mesgctl(void*);
extern void mesgsend(Message*);
extern void mesgdel(Message*, Message*);
extern void mesgmenudel(Window*, Message*, Message*);
extern void mesgmenumark(Window*, char*, char*);
extern void mesgmenumarkdel(Window*, Message*, Message*, int);
extern Message* mesglookup(Message*, char*, char*);
extern Message* mesglookupfile(Message*, char*, char*);
extern void mesgfreeparts(Message*);
extern char* readfile(char*, char*, int*);
extern void ctlprint(int, char*, ...);
extern void* emalloc(uint);
extern char* estrdup(char*);
extern char* estrstrdup(char*, char*);
extern char* egrow(char*, char*, char*);
extern char* eappend(char*, char*, char*);
extern void error(char*, ...);
extern int tokenizec(char*, char**, int, char*);
#pragma varargck argpos error 1
#pragma varargck argpos ctlprint 2
extern Window *wbox;
extern Message mbox;
extern Message replies;
extern char *fsname;
extern int plumbsendfd;
extern int plumbseemailfd;
extern char *home;
extern char *mailboxdir;
extern char deleted[];
extern int wctlfd;
extern int shortmenu;
[-- Attachment #5: mail.c --]
[-- Type: text/plain, Size: 8984 bytes --]
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <plumb.h>
#include "dat.h"
char *maildir = "/mail/fs/"; /* mountpoint of mail file system */
char *mailtermdir = "/mnt/term/mail/fs/"; /* alternate mountpoint */
char *mboxname = "mbox"; /* mailboxdir/mboxname is mail spool file */
char *mailboxdir = nil; /* nil == /mail/box/$user */
char *fsname; /* filesystem for mailboxdir/mboxname is at maildir/fsname */
Window *wbox;
Message mbox;
Message replies;
char *home;
int plumbsendfd;
int plumbseemailfd;
int plumbshowmailfd;
Channel *cplumb;
Channel *cplumbshow;
int wctlfd;
void mainctl(void*);
void plumbproc(void*);
void plumbshowproc(void*);
void plumbthread(void);
void plumbshowthread(void*);
int shortmenu;
void
usage(void)
{
threadprint(2, "usage: Mail [-sS] [mailboxname [directoryname]]\n");
threadexitsall("usage");
}
void
removeupasfs(void)
{
char buf[256];
if(strcmp(mboxname, "mbox") == 0)
return;
snprint(buf, sizeof buf, "close %s", mboxname);
write(mbox.ctlfd, buf, strlen(buf));
}
void
threadmain(int argc, char *argv[])
{
char *s, *user, *name;
char err[ERRLEN], cmd[256];
int i;
/* open these early so we won't miss notification of new mail messages while we read mbox */
plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
plumbseemailfd = plumbopen("seemail", OREAD|OCEXEC);
plumbshowmailfd = plumbopen("showmail", OREAD|OCEXEC);
shortmenu = 0;
ARGBEGIN{
case 's':
shortmenu = 1;
break;
case 'S':
shortmenu = 2;
break;
default:
usage();
}ARGEND
name = "mbox";
if(argc > 0){
i = strlen(argv[0]);
if(argc>2 || i==0)
usage();
if(argv[0][i-1] == '/')
argv[0][i-1] = '\0';
s = strrchr(argv[0], '/');
if(s == nil)
mboxname = estrdup(argv[0]);
else{
*s++ = '\0';
if(*s == '\0')
usage();
mailboxdir = argv[0];
mboxname = estrdup(s);
}
if(argc > 1)
name = argv[1];
else
name = mboxname;
}
user = getenv("user");
if(user == nil)
user = "none";
if(mailboxdir == nil)
mailboxdir = estrstrdup("/mail/box/", user);
if(access(maildir, 0)<0 && access(mailtermdir, 0)==0)
bind(mailtermdir, maildir, MAFTER);
s = estrstrdup(maildir, "ctl");
mbox.ctlfd = open(s, ORDWR|OCEXEC);
if(mbox.ctlfd < 0)
error("Mail: can't open %s: %r\n", s);
fsname = estrdup(name);
if(argc > 0){
s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1);
for(i=0; i<10; i++){
sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname);
if(write(mbox.ctlfd, s, strlen(s)) >= 0)
break;
err[0] = '\0';
errstr(err);
if(strcmp(err, "mbox name in use") != 0)
error("Mail: can't create directory %s for mail: %s\n", name, err);
free(fsname);
fsname = emalloc(strlen(name)+10);
sprint(fsname, "%s-%d", name, i);
}
free(s);
}
s = estrstrdup(fsname, "/");
mbox.name = estrstrdup(maildir, s);
mbox.level= 0;
readmbox(&mbox, maildir, s);
home = getenv("home");
if(home == nil)
home = "/";
wbox = newwindow();
winname(wbox, mbox.name);
wintagwrite(wbox, "Put Mail", 4+4);
threadcreate(mainctl, wbox, STACK);
snprint(cmd, sizeof cmd, "Mail %s", name);
winsetdump(wbox, "/acme/mail", cmd);
mbox.w = wbox;
mesgmenu(wbox, &mbox);
winclean(wbox);
wctlfd = open("/dev/wctl", OWRITE|OCEXEC); /* for acme window */
cplumb = chancreate(sizeof(Plumbmsg*), 0);
cplumbshow = chancreate(sizeof(Plumbmsg*), 0);
/* start plumb reader as separate proc ... */
proccreate(plumbproc, nil, STACK);
proccreate(plumbshowproc, nil, STACK);
threadcreate(plumbshowthread, nil, STACK);
/* ... and use this thread to read the messages */
plumbthread();
}
void
plumbproc(void*)
{
Plumbmsg *m;
threadsetname("plumbproc");
for(;;){
m = plumbrecv(plumbseemailfd);
sendp(cplumb, m);
if(m == nil)
threadexits(nil);
}
}
void
plumbshowproc(void*)
{
Plumbmsg *m;
threadsetname("plumbshowproc");
for(;;){
m = plumbrecv(plumbshowmailfd);
sendp(cplumbshow, m);
if(m == nil)
threadexits(nil);
}
}
void
newmesg(char *name, char *digest)
{
Dir d;
if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
return; /* message is about another mailbox */
if(mesglookupfile(&mbox, name, digest) != nil)
return;
if(dirstat(name, &d) < 0)
return;
if(mesgadd(&mbox, mbox.name, &d, digest))
mesgmenunew(wbox, &mbox);
}
void
showmesg(char *name, char *digest)
{
char *n;
if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
return; /* message is about another mailbox */
n = estrdup(name+strlen(mbox.name));
if(n[strlen(n)-1] != '/')
n = egrow(n, "/", nil);
mesgopen(&mbox, mbox.name, name+strlen(mbox.name), nil, 1, digest);
free(n);
}
void
delmesg(char *name, char *digest)
{
Message *m;
//threadprint(2, "Mail: ignoring delete message for %s\n", name);
m = mesglookupfile(&mbox, name, digest);
if(m != nil)
mesgmenumarkdel(wbox, &mbox, m, 0);
}
void
plumbthread(void)
{
Plumbmsg *m;
Plumbattr *a;
char *type, *digest;
threadsetname("plumbthread");
while((m = recvp(cplumb)) != nil){
a = m->attr;
digest = plumblookup(a, "digest");
type = plumblookup(a, "mailtype");
if(type == nil)
threadprint(2, "Mail: plumb message with no mailtype attribute\n");
else if(strcmp(type, "new") == 0)
newmesg(m->data, digest);
else if(strcmp(type, "delete") == 0)
delmesg(m->data, digest);
else
threadprint(2, "Mail: unknown plumb attribute %s\n", type);
plumbfree(m);
}
threadexits(nil);
}
void
plumbshowthread(void*)
{
Plumbmsg *m;
threadsetname("plumbshowthread");
while((m = recvp(cplumbshow)) != nil){
showmesg(m->data, plumblookup(m->attr, "digest"));
plumbfree(m);
}
threadexits(nil);
}
int
mboxcommand(Window *w, char *s)
{
char *t;
Message *m, *next;
int ok;
while(*s==' ' || *s=='\t' || *s=='\n')
s++;
if(strncmp(s, "Mail", 4) == 0){
s += 4;
while(*s==' ' || *s=='\t' || *s=='\n')
s++;
t = s;
while(*s && *s!=' ' && *s!='\t' && *s!='\n')
s++;
*s = 0;
mkreply(nil, "Mail", t);
return 1;
}
if(strcmp(s, "Del") == 0){
if(mbox.dirty){
mbox.dirty = 0;
threadprint(2, "mail: mailbox not written\n");
return 1;
}
ok = 1;
for(m=mbox.head; m!=nil; m=next){
next = m->next;
if(m->w){
if(windel(m->w, 0))
m->w = nil;
else
ok = 0;
}
}
for(m=replies.head; m!=nil; m=next){
next = m->next;
if(m->w){
if(windel(m->w, 0))
m->w = nil;
else
ok = 0;
}
}
if(ok){
windel(w, 1);
removeupasfs();
threadexitsall(nil);
}
return 1;
}
if(strcmp(s, "Put") == 0){
rewritembox(wbox, &mbox);
return 1;
}
return 0;
}
void
mainctl(void *v)
{
Window *w;
Event *e, *e2, *eq, *ea;
int na, nopen;
char *s, *t, *buf;
w = v;
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 */
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 it's a known command, do it */
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 it's a long message, it can't be for us anyway */
if(!mboxcommand(w, s)) /* send it back */
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;
}
nopen = 0;
do{
/* skip 'deleted' string if present' */
if(strncmp(s, deleted, strlen(deleted)) == 0)
s += strlen(deleted);
/* skip mail box name if present */
if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
s += strlen(mbox.name);
nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil);
while(*s!='\0' && *s++!='\n')
;
}while(*s);
if(nopen == 0) /* send it back */
winwriteevent(w, e);
free(buf);
break;
case 'I': /* modify away; we don't care */
case 'D':
case 'd':
case 'i':
break;
default:
goto Unknown;
}
}
}
}
[-- Attachment #6: mesg.c --]
[-- Type: text/plain, Size: 23475 bytes --]
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <ctype.h>
#include <plumb.h>
#include "dat.h"
enum
{
DIRCHUNK = 32*sizeof(Dir)
};
char regexchars[] = "\\/[].+?()*^$";
char deleted[] = "(deleted)-";
char deletedrx[] = "\\(deleted\\)-";
char deletedrx01[] = "(\\(deleted\\)-)?";
char deletedaddr[] = "-#0;/^\\(deleted\\)-/";
struct{
char *type;
char *port;
char *suffix;
} ports[] = {
"text/", "edit", "txt", /* must be first for plumbport() */
"image/gif", "image", "gif",
"image/jpeg", "image", "jpg",
"application/postscript", "postscript", "ps",
"application/pdf", "postscript", "pdf",
"application/msword", "msword", "doc",
"application/rtf", "msword", "rtf",
nil, nil
};
char *goodtypes[] = {
"text",
"text/plain",
"message/rfc822",
"text/richtext",
"text/tab-separated-values",
"application/octet-stream",
nil,
};
struct{
char *type;
char *ext;
} exts[] = {
"image/gif", ".gif",
"image/jpeg", ".jpg",
nil, nil
};
char*
line(char *data, char **pp)
{
char *p, *q;
for(p=data; *p!='\0' && *p!='\n'; p++)
;
if(*p == '\n')
*pp = p+1;
else
*pp = p;
q = emalloc(p-data + 1);
memmove(q, data, p-data);
return q;
}
void
scanheaders(Message *m, char *dir)
{
char *s, *t, *u;
s = readfile(dir, "header", nil);
if(s != nil)
while(*s){
t = line(s, &s);
if(strncmp(t, "From: ", 6) == 0){
m->fromcolon = estrdup(t+6);
/* remove all quotes; they're ugly and irregular */
for(u=m->fromcolon; *u; u++)
if(*u == '"')
memmove(u, u+1, strlen(u));
}
if(strncmp(t, "Subject: ", 9) == 0)
m->subject = estrdup(t+9);
free(t);
}
if(m->fromcolon == nil)
m->fromcolon = estrdup(m->from);
}
int
loadinfo(Message *m, char *dir)
{
int n;
char *data, *p, *s;
data = readfile(dir, "info", &n);
if(data == nil)
return 0;
m->from = line(data, &p);
scanheaders(m, dir); /* depends on m->from being set */
m->to = line(p, &p);
m->cc = line(p, &p);
m->replyto = line(p, &p);
m->date = line(p, &p);
s = line(p, &p);
if(m->subject == nil)
m->subject = s;
else
free(s);
m->type = line(p, &p);
m->disposition = line(p, &p);
m->filename = line(p, &p);
m->digest = line(p, &p);
free(data);
return 1;
}
int
isnumeric(char *s)
{
while(*s){
if(!isdigit(*s))
return 0;
s++;
}
return 1;
}
Dir*
loaddir(char *name)
{
int m, n, fd;
Dir *dp;
fd = open(name, OREAD);
if(fd < 0)
error("can't open %s: %r\n", name);
dp = nil;
for(n=0; ; n+=m){
dp = realloc(dp, n+DIRCHUNK);
memset(dp+n/sizeof(Dir), 0, DIRCHUNK);
m = dirread(fd, dp+n/sizeof(Dir), DIRCHUNK);
if(m <= 0)
break;
}
close(fd);
return dp;
}
void
readmbox(Message *mbox, char *dir, char *subdir)
{
char *name;
Dir *d, *dirp;
name = estrstrdup(dir, subdir);
dirp = loaddir(name);
mbox->recursed = 1;
for(d=dirp; d->name[0]!='\0'; d++)
if(isnumeric(d->name))
mesgadd(mbox, name, d, nil);
free(dirp);
free(name);
}
/* add message to box, in increasing numerical order */
int
mesgadd(Message *mbox, char *dir, Dir *d, char *digest)
{
Message *m;
char *name;
int loaded;
m = emalloc(sizeof(Message));
m->name = estrstrdup(d->name, "/");
m->next = nil;
m->prev = mbox->tail;
m->level= mbox->level+1;
m->recursed = 0;
name = estrstrdup(dir, m->name);
loaded = loadinfo(m, name);
free(name);
/* if two upas/fs are running, we can get misled, so check digest before accepting message */
if(loaded==0 || (digest!=nil && m->digest!=nil && strcmp(digest, m->digest)!=0)){
mesgfreeparts(m);
free(m);
return 0;
}
if(mbox->tail != nil)
mbox->tail->next = m;
mbox->tail = m;
if(mbox->head == nil)
mbox->head = m;
if (m->level != 1){
m->recursed = 1;
readmbox(m, dir, m->name);
}
return 1;
}
int
thisyear(char *year)
{
static char now[10];
char *s;
if(now[0] == '\0'){
s = ctime(time(nil));
strcpy(now, s+24);
}
return strncmp(year, now, 4) == 0;
}
char*
stripdate(char *as)
{
int n;
char *s, *fld[10];
as = estrdup(as);
s = estrdup(as);
n = tokenize(s, fld, 10);
if(n > 5){
sprint(as, "%.3s ", fld[0]); /* day */
/* some dates have 19 Apr, some Apr 19 */
if(strlen(fld[1])<4 && isnumeric(fld[1]))
sprint(as+strlen(as), "%.3s %.3s ", fld[1], fld[2]); /* date, month */
else
sprint(as+strlen(as), "%.3s %.3s ", fld[2], fld[1]); /* date, month */
/* do we use time or year? depends on whether year matches this one */
if(thisyear(fld[5])){
if(strchr(fld[3], ':') != nil)
sprint(as+strlen(as), "%.5s ", fld[3]); /* time */
else if(strchr(fld[4], ':') != nil)
sprint(as+strlen(as), "%.5s ", fld[4]); /* time */
}else
sprint(as+strlen(as), "%.4s ", fld[5]); /* year */
}
free(s);
return as;
}
char*
readfile(char *dir, char *name, int *np)
{
char *file, *data;
int fd;
Dir d;
if(np != nil)
*np = 0;
file = estrstrdup(dir, name);
fd = open(file, OREAD);
if(fd < 0)
return nil;
dirfstat(fd, &d);
free(file);
data = emalloc(d.length+1);
read(fd, data, d.length);
close(fd);
if(np != nil)
*np = d.length;
return data;
}
char*
info(Message *m, int ind)
{
char *i;
int j;
if (ind == 0 && shortmenu) {
char fmt[80];
char s[80];
int len;
int lens;
len = (shortmenu > 1) ? 10 : 30;
lens = (shortmenu > 1) ? 25 : 30;
if (ind == 0 && m->subject[0] == '\0'){
snprint(fmt, 80, " %%-%d.%ds", len, len);
snprint(s, 80, fmt, m->fromcolon);
}else{
snprint(fmt, 80, " %%-%d.%ds %%-%d.%ds", len, len, lens, lens);
snprint(s, 80, fmt, m->fromcolon, m->subject);
}
i = estrdup(s);
return i;
}
i = estrdup("");
i = eappend(i, "\t", m->fromcolon);
i = egrow(i, "\t", stripdate(m->date));
if(ind == 0){
if(strcmp(m->type, "text")!=0 && strncmp(m->type, "text/", 5)!=0 &&
strncmp(m->type, "multipart/", 10)!=0)
i = egrow(i, "\t(", estrstrdup(m->type, ")"));
}else if(strncmp(m->type, "multipart/", 10) != 0)
i = egrow(i, "\t(", estrstrdup(m->type, ")"));
if(m->subject[0] != '\0'){
i = eappend(i, "\n", nil);
for(j=0; j<ind; j++)
i = eappend(i, "\t", nil);
i = eappend(i, "\t", m->subject);
}
return i;
}
void
mesgmenu0(Window *w, Message *mbox, char *realdir, char *dir, int ind, Biobuf *fd, int onlyone, int dotail)
{
int i;
Message *m;
char *name, *tmp;
/* show mail box in reverse order, pieces in forward order */
if(ind > 0)
m = mbox->head;
else
m = mbox->tail;
while(m != nil){
for(i=0; i<ind; i++)
Bprint(fd, "\t");
if(ind != 0)
Bprint(fd, " ");
name = estrstrdup(dir, m->name);
tmp = info(m, ind);
Bprint(fd, "%s%s\n", name, tmp);
free(tmp);
if(dotail && m->tail)
mesgmenu0(w, m, realdir, name, ind+1, fd, 0, dotail);
free(name);
if(ind)
m = m->next;
else
m = m->prev;
if(onlyone)
m = nil;
}
}
void
mesgmenu(Window *w, Message *mbox)
{
winopenbody(w, OWRITE);
mesgmenu0(w, mbox, mbox->name, "", 0, w->body, 0, !shortmenu);
winclosebody(w);
}
/* one new message has arrived, as mbox->tail */
void
mesgmenunew(Window *w, Message *mbox)
{
Biobuf *b;
winselect(w, "0", 0);
w->data = winopenfile(w, "data");
b = emalloc(sizeof(Biobuf));
Binit(b, w->data, OWRITE);
mesgmenu0(w, mbox, mbox->name, "", 0, b, 1, !shortmenu);
Bterm(b);
if(!mbox->dirty)
winclean(w);
/* select tag line plus following indented lines, but not final newline (it's distinctive) */
winselect(w, "0/.*\\n((\t.*\\n)*\t.*)?/", 1);
close(w->addr);
close(w->data);
w->addr = -1;
w->data = -1;
}
char*
name2regexp(char *prefix, char *s)
{
char *buf, *p, *q;
buf = emalloc(strlen(prefix)+2*strlen(s)+50); /* leave room to append more */
p = buf;
*p++ = '0';
*p++ = '/';
*p++ = '^';
strcpy(p, prefix);
p += strlen(prefix);
for(q=s; *q!='\0'; q++){
if(strchr(regexchars, *q) != nil)
*p++ = '\\';
*p++ = *q;
}
*p++ = '/';
*p = '\0';
return buf;
}
void
mesgmenumarkdel(Window *w, Message *mbox, Message *m, int writeback)
{
char *buf;
if(m->deleted)
return;
m->writebackdel = writeback;
if(w->data < 0)
w->data = winopenfile(w, "data");
buf = name2regexp("", m->name);
strcat(buf, "-#0");
if(winselect(w, buf, 1))
write(w->data, deleted, 10);
free(buf);
close(w->data);
close(w->addr);
w->addr = w->data = -1;
mbox->dirty = 1;
m->deleted = 1;
}
void
mesgmenumarkundel(Window *w, Message*, Message *m)
{
char *buf;
if(m->deleted == 0)
return;
if(w->data < 0)
w->data = winopenfile(w, "data");
buf = name2regexp(deletedrx, m->name);
if(winselect(w, buf, 1))
if(winsetaddr(w, deletedaddr, 1))
write(w->data, "", 0);
free(buf);
close(w->data);
close(w->addr);
w->addr = w->data = -1;
m->deleted = 0;
}
void
mesgmenudel(Window *w, Message *mbox, Message *m)
{
char *buf;
if(w->data < 0)
w->data = winopenfile(w, "data");
buf = name2regexp(deletedrx, m->name);
if(winsetaddr(w, buf, 1) && winsetaddr(w, ".,./.*\\n(\t.*\\n)*/", 1))
write(w->data, "", 0);
free(buf);
close(w->data);
close(w->addr);
w->addr = w->data = -1;
mbox->dirty = 1;
m->deleted = 1;
}
void
mesgmenumark(Window *w, char *which, char *mark)
{
char *buf;
if(w->data < 0)
w->data = winopenfile(w, "data");
buf = name2regexp(deletedrx01, which);
if(winsetaddr(w, buf, 1) && winsetaddr(w, "+0-#1", 1)) /* go to end of line */
write(w->data, mark, strlen(mark));
free(buf);
close(w->data);
close(w->addr);
w->addr = w->data = -1;
if(!mbox.dirty)
winclean(w);
}
void
mesgfreeparts(Message *m)
{
free(m->name);
free(m->replyname);
free(m->fromcolon);
free(m->from);
free(m->to);
free(m->cc);
free(m->replyto);
free(m->date);
free(m->subject);
free(m->type);
free(m->disposition);
free(m->filename);
free(m->digest);
}
void
mesgdel(Message *mbox, Message *m)
{
Message *n, *next;
if(m->opened)
error("Mail: internal error: deleted message still open in mesgdel\n");
/* delete subparts */
for(n=m->head; n!=nil; n=next){
next = n->next;
mesgdel(m, n);
}
/* remove this message from list */
if(m->next)
m->next->prev = m->prev;
else
mbox->tail = m->prev;
if(m->prev)
m->prev->next = m->next;
else
mbox->head = m->next;
mesgfreeparts(m);
}
int
mesgsave(Message *m, char *s)
{
int ofd, n, k, ret;
char *t, *raw, *unixheader, *all;
t = estrstrdup(mbox.name, m->name);
raw = readfile(t, "raw", &n);
unixheader = readfile(t, "unixheader", &k);
if(raw==nil || unixheader==nil){
threadprint(2, "Mail: can't read %s: %r\n", t);
free(t);
return 0;
}
free(t);
all = emalloc(n+k+1);
memmove(all, unixheader, k);
memmove(all+k, raw, n);
memmove(all+k+n, "\n", 1);
n = k+n+1;
free(unixheader);
free(raw);
ret = 1;
s = estrdup(s);
if(s[0] != '/')
s = egrow(estrdup(mailboxdir), "/", s);
ofd = open(s, OWRITE);
if(ofd < 0){
threadprint(2, "Mail: can't open %s: %r\n", s);
ret = 0;
}else if(seek(ofd, 0LL, 2)<0 || write(ofd, all, n)!=n){
threadprint(2, "Mail: save failed: can't write %s: %r\n", s);
ret = 0;
}
free(all);
close(ofd);
free(s);
return ret;
}
int
mesgcommand(Message *m, char *cmd)
{
char *s, *t, *u;
int ok, ret;
s = cmd;
ret = 1;
while(*s==' ' || *s=='\t' || *s=='\n')
s++;
if(strcmp(s, "Post") == 0){
mesgsend(m);
goto Return;
}
if(strncmp(s, "Save", 4) == 0){
if(m->isreply)
goto Return;
s += 4;
while(*s==' ' || *s=='\t' || *s=='\n')
s++;
u = estrdup("\t[saved");
if(*s == '\0'){
ok = mesgsave(m, "stored");
}else{
t = s;
while(*s!='\0' && *s!=' ' && *s!='\t' && *s!='\n')
s++;
*s = 0;
ok = mesgsave(m, t);
u = eappend(u, " ", t);
}
if(ok){
u = egrow(u, "]", nil);
mesgmenumark(mbox.w, m->name, u);
}
free(u);
goto Return;
}
if(strcmp(s, "Reply") == 0){
mkreply(m, s, nil);
goto Return;
}
if(strncmp(s, "Reply all", 9) == 0 || strcmp(s, "Replyall") == 0){
mkreply(m, "Replyall", nil);
goto Return;
}
if(strcmp(s, "Del") == 0){
if(windel(m->w, 0)){
chanfree(m->w->cevent);
free(m->w);
m->w = nil;
if(m->isreply)
delreply(m);
else{
m->opened = 0;
m->tagposted = 0;
}
free(cmd);
threadexits(nil);
}
goto Return;
}
if(strcmp(s, "Delmesg") == 0){
if(!m->isreply){
mesgmenumarkdel(wbox, &mbox, m, 1);
free(cmd); /* mesgcommand might not return */
mesgcommand(m, estrdup("Del"));
return 1;
}
goto Return;
}
if(strcmp(s, "UnDelmesg") == 0){
if(!m->isreply && m->deleted)
mesgmenumarkundel(wbox, &mbox, m);
goto Return;
}
// if(strcmp(s, "Headers") == 0){
// m->showheaders();
// return True;
// }
ret = 0;
Return:
free(cmd);
return ret;
}
void
mesgtagpost(Message *m)
{
if(m->tagposted)
return;
wintagwrite(m->w, " Post", 5);
m->tagposted = 1;
}
void
mesgctl(void *v)
{
Message *m;
Window *w;
Event *e, *eq, *e2, *ea;
int na, nopen, i, j;
char *s, *t, *buf;
m = v;
w = m->w;
threadsetname("mesgctl");
proccreate(wineventproc, w, STACK);
for(;;){
e = recvp(w->cevent);
switch(e->c1){
default:
Unk:
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 */
case 'M':
switch(e->c2){
case 'x': /* mouse only */
case 'X':
ea = nil;
eq = e;
if(e->flag & 2){
e2 = recvp(w->cevent);
eq = e2;
}
if(e->flag & 8){
ea = recvp(w->cevent);
recvp(w->cevent);
na = ea->nb;
}else
na = 0;
if(eq->q1>eq->q0 && eq->nb==0){
s = emalloc((eq->q1-eq->q0)*UTFmax+1);
winread(w, eq->q0, eq->q1, s);
}else
s = estrdup(eq->b);
if(na){
t = emalloc(strlen(s)+1+na+1);
sprint(t, "%s %s", s, ea->b);
free(s);
s = t;
}
if(!mesgcommand(m, s)) /* send it back */
winwriteevent(w, e);
break;
case 'l': /* mouse only */
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;
}
nopen = 0;
do{
/* skip mail box name if present */
if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
s += strlen(mbox.name);
if(strstr(s, "body") != nil){
/* strip any known extensions */
for(i=0; exts[i].ext!=nil; i++){
j = strlen(exts[i].ext);
if(strlen(s)>j && strcmp(s+strlen(s)-j, exts[i].ext)==0){
s[strlen(s)-j] = '\0';
break;
}
}
if(strlen(s)>5 && strcmp(s+strlen(s)-5, "/body")==0)
s[strlen(s)-4] = '\0'; /* leave / in place */
}
nopen += mesgopen(&mbox, mbox.name, s, m, 0, nil);
while(*s!=0 && *s++!='\n')
;
}while(*s);
if(nopen == 0) /* send it back */
winwriteevent(w, e);
free(buf);
break;
case 'I': /* modify away; we don't care */
case 'D':
mesgtagpost(m);
/* fall through */
case 'd':
case 'i':
break;
default:
goto Unk;
}
}
}
}
void
mesgline(Message *m, char *header, char *value)
{
if(strlen(value) > 0)
Bprint(m->w->body, "%s: %s\n", header, value);
}
int
isprintable(char *type)
{
int i;
for(i=0; goodtypes[i]!=nil; i++)
if(strcmp(type, goodtypes[i])==0)
return 1;
return 0;
}
char*
ext(char *type)
{
int i;
for(i=0; exts[i].type!=nil; i++)
if(strcmp(type, exts[i].type)==0)
return exts[i].ext;
return "";
}
void
mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly)
{
char *dest;
if(strcmp(m->disposition, "file")==0 || strlen(m->filename)!=0){
if(strlen(m->filename) == 0){
dest = estrdup(m->name);
dest[strlen(dest)-1] = '\0';
}else
dest = estrdup(m->filename);
if(m->filename[0] != '/')
dest = egrow(estrdup("/tmp"), "/", dest);
Bprint(w->body, "\tcp %s%sbody%s %s\n", rootdir, name, ext(m->type), dest);
free(dest);
}else if(!fileonly)
Bprint(w->body, "\tfile is %s%sbody%s\n", rootdir, name, ext(m->type));
}
void
mesgload(Message *m, char *rootdir, char *file, Window *w)
{
char *s, *subdir, *name, *dir;
Message *mp, *thisone;
int n;
dir = estrstrdup(rootdir, file);
if(strlen(m->from) > 0)
Bprint(w->body, "From: %s\n", m->from);
else
Bprint(w->body, "\n");
mesgline(m, "Date", m->date);
mesgline(m, "To", m->to);
mesgline(m, "CC", m->cc);
mesgline(m, "Subject", m->subject);
Bprint(w->body, "\n");
if(m->level == 1 && m->recursed == 0){
m->recursed = 1;
readmbox(m, rootdir, m->name);
}
if(m->head == nil){ /* single part message */
if(strcmp(m->type, "text")==0 || strncmp(m->type, "text/", 5)==0){
mimedisplay(m, m->name, rootdir, w, 1);
s = readfile(dir, "body", &n);
winwritebody(w, s, n);
free(s);
}else
mimedisplay(m, m->name, rootdir, w, 0);
}else{ /* multi-part message */
thisone = nil;
if(strcmp(m->type, "multipart/alternative") == 0){
thisone = m->head; /* in case we can't find a good one */
for(mp=m->head; mp!=nil; mp=mp->next)
if(isprintable(mp->type)){
thisone = mp;
break;
}
}
for(mp=m->head; mp!=nil; mp=mp->next){
if(thisone!=nil && mp!=thisone)
continue;
subdir = estrstrdup(dir, mp->name);
name = estrstrdup(file, mp->name);
/* skip first element in name because it's already in window name */
if(mp != m->head)
Bprint(w->body, "\n===> %s (%s) [%s]\n", strchr(name, '/')+1, mp->type, mp->disposition);
if(strcmp(mp->type, "text")==0 || strncmp(mp->type, "text/", 5)==0){
mimedisplay(mp, name, rootdir, w, 1);
s = readfile(subdir, "header", &n);
winwritebody(w, s, n);
free(s);
winwritebody(w, "\n", 1);
s = readfile(subdir, "body", &n);
winwritebody(w, s, n);
free(s);
}else{
if(strncmp(mp->type, "multipart/", 10)==0){
mp->w = w;
mesgload(mp, rootdir, name, w);
mp->w = nil;
}else
mimedisplay(mp, name, rootdir, w, 0);
}
free(name);
free(subdir);
}
}
free(dir);
}
int
tokenizec(char *str, char **args, int max, char *splitc)
{
int na;
int intok = 0;
if(max <= 0)
return 0;
for(na=0; *str != '\0';str++){
if(strchr(splitc, *str) == nil){
if(intok)
continue;
args[na++] = str;
intok = 1;
}else{
/* it's a separator/skip character */
*str = '\0';
if(intok){
intok = 0;
if(na >= max)
break;
}
}
}
return na;
}
Message*
mesglookup(Message *mbox, char *name, char *digest)
{
int n;
Message *m;
char *t;
if(digest){
/* can find exactly */
for(m=mbox->head; m!=nil; m=m->next)
if(strcmp(digest, m->digest) == 0)
break;
return m;
}
n = strlen(name);
if(n == 0)
return nil;
if(name[n-1] == '/')
t = estrdup(name);
else
t = estrstrdup(name, "/");
for(m=mbox->head; m!=nil; m=m->next)
if(strcmp(t, m->name) == 0)
break;
free(t);
return m;
}
int
plumbport(char *s)
{
int i;
for(i=0; ports[i].type!=nil; i++)
if(strncmp(s, ports[i].type, strlen(ports[i].type)) == 0)
return i;
/* see if it's a text type */
for(i=0; goodtypes[i]!=nil; i++)
if(strncmp(s, goodtypes[i], strlen(goodtypes[i])) == 0)
return 0;
return -1;
}
void
plumb(Message *m, char *dir)
{
int i;
char *port;
Plumbmsg *pm;
if(strlen(m->type) == 0)
return;
i = plumbport(m->type);
if(i < 0)
threadprint(2, "can't find destination for message subpart\n");
else{
port = ports[i].port;
pm = emalloc(sizeof(Plumbmsg));
pm->src = estrdup("Mail");
if(port)
pm->dst = estrdup(port);
else
pm->dst = nil;
pm->wdir = nil;
pm->type = estrdup("text");
pm->ndata = -1;
pm->data = estrstrdup(dir, "body");
pm->data = eappend(pm->data, ".", ports[i].suffix);
if(plumbsend(plumbsendfd, pm) < 0)
threadprint(2, "error writing plumb message: %r\n");
plumbfree(pm);
}
}
int
mesgopen(Message *mbox, char *dir, char *s, Message *mesg, int plumbed, char *digest)
{
char *t, *u, *v;
Message *m;
char *direlem[10];
int i, ndirelem;
/* find white-space-delimited first word */
for(t=s; *t!='\0' && !isspace(*t); t++)
;
u = emalloc(t-s+1);
memmove(u, s, t-s);
/* separate it on slashes */
ndirelem = tokenizec(u, direlem, nelem(direlem), "/");
if(ndirelem <= 0){
Error:
free(u);
return 0;
}
if(plumbed){
write(wctlfd, "top", 3);
write(wctlfd, "current", 7);
}
/* open window for message */
m = mesglookup(mbox, direlem[0], digest);
if(m == nil)
goto Error;
if(mesg!=nil && m!=mesg) /* string looked like subpart but isn't part of this message */
goto Error;
if(m->opened == 0){
m->w = newwindow();
v = estrstrdup(mbox->name, m->name);
winname(m->w, v);
free(v);
if(m->deleted)
wintagwrite(m->w, "Reply all UnDelmesg Save ", 6+4+10+5);
else
wintagwrite(m->w, "Reply all Delmesg Save ", 6+4+8+5);
threadcreate(mesgctl, m, STACK);
winopenbody(m->w, OWRITE);
mesgload(m, dir, m->name, m->w);
winclosebody(m->w);
winclean(m->w);
m->opened = 1;
if(ndirelem == 1){
free(u);
return 1;
}
}
if(ndirelem == 1 && plumbport(m->type) <= 0){
/* make sure dot is visible */
ctlprint(m->w->ctl, "show\n");
return 0;
}
/* walk to subpart */
dir = estrstrdup(dir, m->name);
for(i=1; i<ndirelem; i++){
m = mesglookup(m, direlem[i], digest);
if(m == nil)
break;
dir = egrow(dir, m->name, nil);
}
if(m != nil && plumbport(m->type) > 0)
plumb(m, dir);
free(dir);
free(u);
return 1;
}
void
rewritembox(Window *w, Message *mbox)
{
Message *m, *next;
char *deletestr, *t;
int nopen;
deletestr = estrstrdup("delete ", fsname);
nopen = 0;
for(m=mbox->head; m!=nil; m=next){
next = m->next;
if(m->deleted == 0)
continue;
if(m->opened){
nopen++;
continue;
}
if(m->writebackdel){
/* messages deleted by plumb message are not removed again */
t = estrdup(m->name);
if(strlen(t) > 0)
t[strlen(t)-1] = '\0';
deletestr = egrow(deletestr, " ", t);
}
mesgmenudel(w, mbox, m);
mesgdel(mbox, m);
}
if(write(mbox->ctlfd, deletestr, strlen(deletestr)) < 0)
threadprint(2, "Mail: warning: error removing mail message files: %r\n");
free(deletestr);
winselect(w, "0", 0);
if(nopen == 0)
winclean(w);
mbox->dirty = 0;
}
/* name is a full file name, but it might not belong to us */
Message*
mesglookupfile(Message *mbox, char *name, char *digest)
{
int k, n;
k = strlen(name);
n = strlen(mbox->name);
if(k==0 || strncmp(name, mbox->name, n) != 0){
// threadprint(2, "Mail: message %s not in this mailbox\n", name);
return nil;
}
return mesglookup(mbox, name+n, digest);
}
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2001-05-03 13:24 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-05-03 10:37 [9fans] tiny patches for cmds on the bitsy nemo
2001-05-03 13:24 nemo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).