initial faces (John Cummings)

This commit is contained in:
rsc 2005-10-31 14:47:39 +00:00
parent 663ddde9d0
commit b330c942b4
7 changed files with 1909 additions and 0 deletions

30
src/cmd/faces/dblook.c Normal file
View file

@ -0,0 +1,30 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <plumb.h>
#include <regexp.h>
#include <bio.h>
#include <9pclient.h>
#include <thread.h>
#include "faces.h"
void
threadmain(int argc, char **argv)
{
Face f;
char *q;
if(argc != 3){
fprint(2, "usage: dblook name domain\n");
threadexitsall("usage");
}
q = findfile(&f, argv[2], argv[1]);
print("%s\n", q);
}
void
killall(char *s)
{
USED(s);
}

562
src/cmd/faces/facedb.c Normal file
View file

@ -0,0 +1,562 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <plumb.h>
#include <regexp.h>
#include <bio.h>
#include <9pclient.h>
#include "faces.h"
enum /* number of deleted faces to cache */
{
Nsave = 20,
};
static Facefile *facefiles;
static int nsaved;
static char *facedom;
/*
* Loading the files is slow enough on a dial-up line to be worth this trouble
*/
typedef struct Readcache Readcache;
struct Readcache {
char *file;
char *data;
long mtime;
long rdtime;
Readcache *next;
};
static Readcache *rcache;
ulong
dirlen(char *s)
{
Dir *d;
ulong len;
d = dirstat(s);
if(d == nil)
return 0;
len = d->length;
free(d);
return len;
}
ulong
fsdirlen(CFsys *fs,char *s)
{
Dir *d;
ulong len;
d = fsdirstat(fs,s);
if(d == nil)
return 0;
len = d->length;
free(d);
return len;
}
ulong
dirmtime(char *s)
{
Dir *d;
ulong t;
d = dirstat(s);
if(d == nil)
return 0;
t = d->mtime;
free(d);
return t;
}
static char*
doreadfile(char *s)
{
char *p;
int fd, n;
ulong len;
len = dirlen(s);
if(len == 0)
return nil;
p = malloc(len+1);
if(p == nil)
return nil;
if((fd = open(s, OREAD)) < 0
|| (n = readn(fd, p, len)) < 0) {
close(fd);
free(p);
return nil;
}
p[n] = '\0';
return p;
}
static char*
readfile(char *s)
{
Readcache *r, **l;
char *p;
ulong mtime;
for(l=&rcache, r=*l; r; l=&r->next, r=*l) {
if(strcmp(r->file, s) != 0)
continue;
/*
* if it's less than 30 seconds since we read it, or it
* hasn't changed, send back our copy
*/
if(time(0) - r->rdtime < 30)
return strdup(r->data);
if(dirmtime(s) == r->mtime) {
r->rdtime = time(0);
return strdup(r->data);
}
/* out of date, remove this and fall out of loop */
*l = r->next;
free(r->file);
free(r->data);
free(r);
break;
}
/* add to cache */
mtime = dirmtime(s);
if(mtime == 0)
return nil;
if((p = doreadfile(s)) == nil)
return nil;
r = malloc(sizeof(*r));
if(r == nil)
return nil;
r->mtime = mtime;
r->file = estrdup(s);
r->data = p;
r->rdtime = time(0);
r->next = rcache;
rcache = r;
return strdup(r->data);
}
static char*
translatedomain(char *dom)
{
static char buf[200];
char *p, *ep, *q, *nextp, *file;
char *bbuf, *ebuf;
Reprog *exp;
if(dom == nil || *dom == 0)
return nil;
if((file = readfile(unsharp("#9/lib/face/.machinelist"))) == nil)
return dom;
for(p=file; p; p=nextp) {
if(nextp = strchr(p, '\n'))
*nextp++ = '\0';
if(*p == '#' || (q = strpbrk(p, " \t")) == nil || q-p > sizeof(buf)-2)
continue;
bbuf = buf+1;
ebuf = buf+(1+(q-p));
strncpy(bbuf, p, ebuf-bbuf);
*ebuf = 0;
if(*bbuf != '^')
*--bbuf = '^';
if(ebuf[-1] != '$') {
*ebuf++ = '$';
*ebuf = 0;
}
if((exp = regcomp(bbuf)) == nil){
fprint(2, "bad regexp in machinelist: %s\n", bbuf);
killall("regexp");
}
if(regexec(exp, dom, 0, 0)){
free(exp);
ep = p+strlen(p);
q += strspn(q, " \t");
if(ep-q+2 > sizeof buf) {
fprint(2, "huge replacement in machinelist: %.*s\n", utfnlen(q, ep-q), q);
exits("bad big replacement");
}
strncpy(buf, q, ep-q);
ebuf = buf+(ep-q);
*ebuf = 0;
while(ebuf > buf && (ebuf[-1] == ' ' || ebuf[-1] == '\t'))
*--ebuf = 0;
free(file);
return buf;
}
free(exp);
}
free(file);
return dom;
}
static char*
tryfindpicture_user(char *dom, char *user, int depth)
{
static char buf[200];
char *p, *q, *nextp, *file, *usr;
usr = getuser();
sprint(buf, "/usr/%s/lib/face/48x48x%d/.dict", usr, depth);
if((file = readfile(buf)) == nil)
return nil;
snprint(buf, sizeof buf, "%s/%s", dom, user);
for(p=file; p; p=nextp) {
if(nextp = strchr(p, '\n'))
*nextp++ = '\0';
if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
continue;
*q++ = 0;
if(strcmp(buf, p) == 0) {
q += strspn(q, " \t");
q = buf+snprint(buf, sizeof buf, "/usr/%s/lib/face/48x48x%d/%s", usr, depth, q);
while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
*--q = 0;
free(file);
return buf;
}
}
free(file);
return nil;
}
static char*
tryfindpicture_global(char *dom, char *user, int depth)
{
static char buf[200];
char *p, *q, *nextp, *file;
sprint(buf, "#9/lib/face/48x48x%d/.dict", depth);
if((file = readfile(unsharp(buf))) == nil)
return nil;
snprint(buf, sizeof buf, "%s/%s", dom, user);
for(p=file; p; p=nextp) {
if(nextp = strchr(p, '\n'))
*nextp++ = '\0';
if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
continue;
*q++ = 0;
if(strcmp(buf, p) == 0) {
q += strspn(q, " \t");
q = buf+snprint(buf, sizeof buf, "#9/lib/face/48x48x%d/%s", depth, q);
while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
*--q = 0;
free(file);
return unsharp(buf);
}
}
free(file);
return nil;
}
static char*
tryfindpicture(char *dom, char *user, int depth)
{
char* result;
if((result = tryfindpicture_user(dom, user, depth)) != nil)
return result;
return tryfindpicture_global(dom, user, depth);
}
static char*
tryfindfile(char *dom, char *user, int depth)
{
char *p, *q;
for(;;){
for(p=dom; p; (p=strchr(p, '.')) && p++)
if(q = tryfindpicture(p, user, depth))
return q;
depth >>= 1;
if(depth == 0)
break;
}
return nil;
}
char*
findfile(Face *f, char *dom, char *user)
{
char *p;
int depth;
if(facedom == nil){
facedom = getenv("facedom");
if(facedom == nil)
facedom = DEFAULT;
}
dom = translatedomain(dom);
if(dom == nil)
dom = facedom;
if(screen == nil)
depth = 8;
else
depth = screen->depth;
if(depth > 8)
depth = 8;
f->unknown = 0;
if(p = tryfindfile(dom, user, depth))
return p;
f->unknown = 1;
p = tryfindfile(dom, "unknown", depth);
if(p != nil || strcmp(dom, facedom)==0)
return p;
return tryfindfile("unknown", "unknown", depth);
}
static
void
clearsaved(void)
{
Facefile *f, *next, **lf;
lf = &facefiles;
for(f=facefiles; f!=nil; f=next){
next = f->next;
if(f->ref > 0){
*lf = f;
lf = &(f->next);
continue;
}
if(f->image != display->black && f->image != display->white)
freeimage(f->image);
free(f->file);
free(f);
}
*lf = nil;
nsaved = 0;
}
void
freefacefile(Facefile *f)
{
if(f==nil || f->ref-->1)
return;
if(++nsaved > Nsave)
clearsaved();
}
static Image*
myallocimage(ulong chan)
{
Image *img;
img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
if(img == nil){
clearsaved();
img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
if(img == nil)
return nil;
}
return img;
}
static Image*
readbit(int fd, ulong chan)
{
char buf[4096], hx[4], *p;
uchar data[Facesize*Facesize]; /* more than enough */
int nhx, i, n, ndata, nbit;
Image *img;
n = readn(fd, buf, sizeof buf);
if(n <= 0)
return nil;
if(n >= sizeof buf)
n = sizeof(buf)-1;
buf[n] = '\0';
n = 0;
nhx = 0;
nbit = chantodepth(chan);
ndata = (Facesize*Facesize*nbit)/8;
p = buf;
while(n < ndata) {
p = strpbrk(p+1, "0123456789abcdefABCDEF");
if(p == nil)
break;
if(p[0] == '0' && p[1] == 'x')
continue;
hx[nhx] = *p;
if(++nhx == 2) {
hx[nhx] = 0;
i = strtoul(hx, 0, 16);
data[n++] = i;
nhx = 0;
}
}
if(n < ndata)
return allocimage(display, Rect(0,0,Facesize,Facesize), CMAP8, 0, 0x88888888);
img = myallocimage(chan);
if(img == nil)
return nil;
loadimage(img, img->r, data, ndata);
return img;
}
static Facefile*
readface(char *fn)
{
int x, y, fd;
uchar bits;
uchar *p;
Image *mask;
Image *face;
char buf[16];
uchar data[Facesize*Facesize];
uchar mdata[(Facesize*Facesize)/8];
Facefile *f;
Dir *d;
for(f=facefiles; f!=nil; f=f->next){
if(strcmp(fn, f->file) == 0){
if(f->image == nil)
break;
if(time(0) - f->rdtime >= 30) {
if(dirmtime(fn) != f->mtime){
f = nil;
break;
}
f->rdtime = time(0);
}
f->ref++;
return f;
}
}
if((fd = open(fn, OREAD)) < 0)
return nil;
if(readn(fd, buf, sizeof buf) != sizeof buf){
close(fd);
return nil;
}
seek(fd, 0, 0);
mask = nil;
if(buf[0] == '0' && buf[1] == 'x'){
/* greyscale faces are just masks that we draw black through! */
if(buf[2+8] == ',') /* ldepth 1 */
mask = readbit(fd, GREY2);
else
mask = readbit(fd, GREY1);
face = display->black;
}else{
face = readimage(display, fd, 0);
if(face == nil)
goto Done;
else if(face->chan == GREY4 || face->chan == GREY8){ /* greyscale: use inversion as mask */
mask = myallocimage(face->chan);
/* okay if mask is nil: that will copy the image white background and all */
if(mask == nil)
goto Done;
/* invert greyscale image */
draw(mask, mask->r, display->white, nil, ZP);
gendraw(mask, mask->r, display->black, ZP, face, face->r.min);
freeimage(face);
face = display->black;
}else if(face->depth == 8){ /* snarf the bytes back and do a fill. */
mask = myallocimage(GREY1);
if(mask == nil)
goto Done;
if(unloadimage(face, face->r, data, Facesize*Facesize) != Facesize*Facesize){
freeimage(mask);
goto Done;
}
bits = 0;
p = mdata;
for(y=0; y<Facesize; y++){
for(x=0; x<Facesize; x++){
bits <<= 1;
if(data[Facesize*y+x] != 0xFF)
bits |= 1;
if((x&7) == 7)
*p++ = bits&0xFF;
}
}
if(loadimage(mask, mask->r, mdata, sizeof mdata) != sizeof mdata){
freeimage(mask);
goto Done;
}
}
}
Done:
/* always add at beginning of list, so updated files don't collide in cache */
if(f == nil){
f = emalloc(sizeof(Facefile));
f->file = estrdup(fn);
d = dirfstat(fd);
if(d != nil){
f->mtime = d->mtime;
free(d);
}
f->next = facefiles;
facefiles = f;
}
f->ref++;
f->image = face;
f->mask = mask;
f->rdtime = time(0);
close(fd);
return f;
}
void
findbit(Face *f)
{
char *fn;
fn = findfile(f, f->str[Sdomain], f->str[Suser]);
if(fn) {
if(strstr(fn, "unknown"))
f->unknown = 1;
f->file = readface(fn);
}
if(f->file){
f->bit = f->file->image;
f->mask = f->file->mask;
}else{
/* if returns nil, this is still ok: draw(nil) works */
f->bit = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
replclipr(f->bit, 1, Rect(0, 0, Facesize, Facesize));
f->mask = nil;
}
}

68
src/cmd/faces/faces.h Normal file
View file

@ -0,0 +1,68 @@
enum /* face strings */
{
Suser,
Sdomain,
Sshow,
Sdigest,
Nstring
};
enum
{
Facesize = 48,
};
typedef struct Face Face;
typedef struct Facefile Facefile;
struct Face
{
Image *bit; /* unless there's an error, this is file->image */
Image *mask; /* unless there's an error, this is file->mask */
char *str[Nstring];
int recent;
ulong time;
Tm tm;
int unknown;
Facefile *file;
};
/*
* Loading the files is slow enough on a dial-up line to be worth this trouble
*/
struct Facefile
{
Image *image;
Image *mask;
ulong mtime;
ulong rdtime;
int ref;
char *file;
Facefile *next;
};
extern char date[];
extern char *maildir;
extern char **maildirs;
extern int nmaildirs;
extern CFsys *upasfs;
Face* nextface(void);
void findbit(Face*);
void freeface(Face*);
void initplumb(void);
void killall(char*);
void showmail(Face*);
void delete(char*, char*);
void freefacefile(Facefile*);
Face* dirface(char*, char*);
void resized(void);
int alreadyseen(char*);
ulong dirlen(char*);
ulong fsdirlen(CFsys*, char*);
void *emalloc(ulong);
void *erealloc(void*, ulong);
char *estrdup(char*);
char *findfile(Face*, char*, char*);
void addmaildir(char*);

783
src/cmd/faces/main.c Normal file
View file

@ -0,0 +1,783 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <plumb.h>
#include <regexp.h>
//jpc #include <event.h> /* for support routines only */
#include <bio.h>
#include <thread.h>
#include <mouse.h>
#include <cursor.h>
#include <9pclient.h>
#include "faces.h"
int history = 0; /* use old interface, showing history of mailbox rather than current state */
int initload = 0; /* initialize program with contents of mail box */
enum
{
Facesep = 6, /* must be even to avoid damaging background stipple */
Infolines = 9,
HhmmTime = 18*60*60, /* max age of face to display hh:mm time */
};
enum
{
Mainp,
Timep,
Mousep,
NPROC
};
int pids[NPROC];
char *procnames[] = {
"main",
"time",
"mouse"
};
Rectangle leftright = {0, 0, 20, 15};
uchar leftdata[] = {
0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80,
0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f,
0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0,
0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00,
0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01,
0x80, 0x00, 0x00, 0x80, 0x00
};
uchar rightdata[] = {
0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c,
0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff,
0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0,
0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f,
0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00,
0x18, 0x00, 0x00, 0x10, 0x00
};
CFsys *upasfs;
Mousectl *mousectl;
Image *blue; /* full arrow */
Image *bgrnd; /* pale blue background color */
Image *left; /* left-pointing arrow mask */
Image *right; /* right-pointing arrow mask */
Font *tinyfont;
Font *mediumfont;
Font *datefont;
int first, last; /* first and last visible face; last is first invisible */
int nfaces;
int mousefd;
int nacross;
int ndown;
char date[64];
Face **faces;
char *maildir = "/mail/fs/mbox";
ulong now;
Point datep = { 8, 6 };
Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */
Point enddate; /* where date ends on display; used to place arrows */
Rectangle leftr; /* location of left arrow on display */
Rectangle rightr; /* location of right arrow on display */
void updatetimes(void);
void
setdate(void)
{
now = time(nil);
strcpy(date, ctime(now));
date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */
}
void
init(void)
{
#if 0
mousefd = open("/dev/mouse", OREAD);
if(mousefd < 0){
fprint(2, "faces: can't open mouse: %r\n");
threadexitsall("mouse");
}
#endif
upasfs = nsmount("upasfs",nil);
mousectl = initmouse(nil,screen);
initplumb();
/* make background color */
bgrnd = allocimagemix(display, DPalebluegreen, DWhite);
blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF); /* blue-green */
left = allocimage(display, leftright, GREY1, 0, DWhite);
right = allocimage(display, leftright, GREY1, 0, DWhite);
if(bgrnd==nil || blue==nil || left==nil || right==nil){
fprint(2, "faces: can't create images: %r\n");
threadexitsall("image");
}
loadimage(left, leftright, leftdata, sizeof leftdata);
loadimage(right, leftright, rightdata, sizeof rightdata);
/* initialize little fonts */
tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font");
if(tinyfont == nil)
tinyfont = font;
mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
if(mediumfont == nil)
mediumfont = font;
datefont = font;
facep.y += datefont->height;
if(datefont->height & 1) /* stipple parity */
facep.y++;
faces = nil;
}
void
drawtime(void)
{
Rectangle r;
r.min = addpt(screen->r.min, datep);
if(eqpt(enddate, ZP)){
enddate = r.min;
enddate.x += stringwidth(datefont, "Wed May 30 22:54"); /* nice wide string */
enddate.x += Facesep; /* for safety */
}
r.max.x = enddate.x;
r.max.y = enddate.y+datefont->height;
draw(screen, r, bgrnd, nil, ZP);
string(screen, r.min, display->black, ZP, datefont, date);
}
void
timeproc(void *dummy)
{
for(;;){
lockdisplay(display);
drawtime();
updatetimes();
flushimage(display, 1);
unlockdisplay(display);
sleep(60000);
setdate();
}
}
int
alreadyseen(char *digest)
{
int i;
Face *f;
if(!digest)
return 0;
/* can do accurate check */
for(i=0; i<nfaces; i++){
f = faces[i];
if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0)
return 1;
}
return 0;
}
int
torune(Rune *r, char *s, int nr)
{
int i;
for(i=0; i<nr-1 && *s!='\0'; i++)
s += chartorune(r+i, s);
r[i] = L'\0';
return i;
}
void
center(Font *f, Point p, char *s, Image *color)
{
int i, n, dx;
Rune rbuf[32];
char sbuf[32*UTFmax+1];
dx = stringwidth(f, s);
if(dx > Facesize){
n = torune(rbuf, s, nelem(rbuf));
for(i=0; i<n; i++){
dx = runestringnwidth(f, rbuf, i+1);
if(dx > Facesize)
break;
}
sprint(sbuf, "%.*S", i, rbuf);
s = sbuf;
dx = stringwidth(f, s);
}
p.x += (Facesize-dx)/2;
string(screen, p, color, ZP, f, s);
}
Rectangle
facerect(int index) /* index is geometric; 0 is always upper left face */
{
Rectangle r;
int x, y;
x = index % nacross;
y = index / nacross;
r.min = addpt(screen->r.min, facep);
r.min.x += x*(Facesize+Facesep);
r.min.y += y*(Facesize+Facesep+2*mediumfont->height);
r.max = addpt(r.min, Pt(Facesize, Facesize));
r.max.y += 2*mediumfont->height;
/* simple fix to avoid drawing off screen, allowing customers to use position */
if(index<0 || index>=nacross*ndown)
r.max.x = r.min.x;
return r;
}
static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
char*
facetime(Face *f, int *recent)
{
static char buf[30];
if((long)(now - f->time) > HhmmTime){
*recent = 0;
sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday);
return buf;
}else{
*recent = 1;
sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min);
return buf;
}
}
void
drawface(Face *f, int i)
{
char *tstr;
Rectangle r;
Point p;
if(f == nil)
return;
if(i<first || i>=last)
return;
r = facerect(i-first);
draw(screen, r, bgrnd, nil, ZP);
draw(screen, r, f->bit, f->mask, ZP);
r.min.y += Facesize;
center(mediumfont, r.min, f->str[Suser], display->black);
r.min.y += mediumfont->height;
tstr = facetime(f, &f->recent);
center(mediumfont, r.min, tstr, display->black);
if(f->unknown){
r.min.y -= mediumfont->height + tinyfont->height + 2;
for(p.x=-1; p.x<=1; p.x++)
for(p.y=-1; p.y<=1; p.y++)
center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->white);
center(tinyfont, r.min, f->str[Sdomain], display->black);
}
}
void
updatetimes(void)
{
int i;
Face *f;
for(i=0; i<nfaces; i++){
f = faces[i];
if(f == nil)
continue;
if(((long)(now - f->time) <= HhmmTime) != f->recent)
drawface(f, i);
}
}
void
setlast(void)
{
last = first+nacross*ndown;
if(last > nfaces)
last = nfaces;
}
void
drawarrows(void)
{
Point p;
p = enddate;
p.x += Facesep;
if(p.x & 1)
p.x++; /* align background texture */
leftr = rectaddpt(leftright, p);
p.x += Dx(leftright) + Facesep;
rightr = rectaddpt(leftright, p);
draw(screen, leftr, first>0? blue : bgrnd, left, leftright.min);
draw(screen, rightr, last<nfaces? blue : bgrnd, right, leftright.min);
}
void
addface(Face *f) /* always adds at 0 */
{
Face **ofaces;
Rectangle r0, r1, r;
int y, nx, ny;
if(f == nil)
return;
lockdisplay(display);
if(first != 0){
first = 0;
resized();
}
findbit(f);
nx = nacross;
ny = (nfaces+(nx-1)) / nx;
for(y=ny; y>=0; y--){
/* move them along */
r0 = facerect(y*nx+0);
r1 = facerect(y*nx+1);
r = r1;
r.max.x = r.min.x + (nx - 1)*(Facesize+Facesep);
draw(screen, r, screen, nil, r0.min);
/* copy one down from row above */
if(y != 0){
r = facerect((y-1)*nx+nx-1);
draw(screen, r0, screen, nil, r.min);
}
}
ofaces = faces;
faces = emalloc((nfaces+1)*sizeof(Face*));
memmove(faces+1, ofaces, nfaces*(sizeof(Face*)));
free(ofaces);
nfaces++;
setlast();
drawarrows();
faces[0] = f;
drawface(f, 0);
flushimage(display, 1);
unlockdisplay(display);
}
#if 0
void
loadmboxfaces(char *maildir)
{
int dirfd;
Dir *d;
int i, n;
dirfd = open(maildir, OREAD);
if(dirfd >= 0){
chdir(maildir);
while((n = dirread(dirfd, &d)) > 0){
for(i=0; i<n; i++)
addface(dirface(maildir, d[i].name));
free(d);
}
close(dirfd);
}
}
#endif
void
loadmboxfaces(char *maildir)
{
CFid *dirfd;
Dir *d;
int i, n;
dirfd = fsopen(upasfs,maildir, OREAD);
if(dirfd != nil){
//jpc chdir(maildir);
while((n = fsdirread(dirfd, &d)) > 0){
for(i=0; i<n; i++) {
addface(dirface(maildir, d[i].name));
}
free(d);
}
fsclose(dirfd);
}
else {
error("cannot open %s: %r",maildir);
}
}
void
freeface(Face *f)
{
int i;
if(f->file!=nil && f->bit!=f->file->image)
freeimage(f->bit);
freefacefile(f->file);
for(i=0; i<Nstring; i++)
free(f->str[i]);
free(f);
}
void
delface(int j)
{
Rectangle r0, r1, r;
int nx, ny, x, y;
if(j < first)
first--;
else if(j < last){
nx = nacross;
ny = (nfaces+(nx-1)) / nx;
x = (j-first)%nx;
for(y=(j-first)/nx; y<ny; y++){
if(x != nx-1){
/* move them along */
r0 = facerect(y*nx+x);
r1 = facerect(y*nx+x+1);
r = r0;
r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
draw(screen, r, screen, nil, r1.min);
}
if(y != ny-1){
/* copy one up from row below */
r = facerect((y+1)*nx);
draw(screen, facerect(y*nx+nx-1), screen, nil, r.min);
}
x = 0;
}
if(last < nfaces) /* first off-screen becomes visible */
drawface(faces[last], last-1);
else{
/* clear final spot */
r = facerect(last-first-1);
draw(screen, r, bgrnd, nil, r.min);
}
}
freeface(faces[j]);
memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
nfaces--;
setlast();
drawarrows();
}
void
dodelete(int i)
{
Face *f;
f = faces[i];
if(history){
free(f->str[Sshow]);
f->str[Sshow] = estrdup("");
}else{
delface(i);
flushimage(display, 1);
}
}
void
delete(char *s, char *digest)
{
int i;
Face *f;
lockdisplay(display);
for(i=0; i<nfaces; i++){
f = faces[i];
if(digest != nil){
if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
dodelete(i);
break;
}
}else{
if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
dodelete(i);
break;
}
}
}
unlockdisplay(display);
}
void
faceproc(void)
{
for(;;)
addface(nextface());
}
void
resized(void)
{
int i;
nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
;
setlast();
draw(screen, screen->r, bgrnd, nil, ZP);
enddate = ZP;
drawtime();
for(i=0; i<nfaces; i++)
drawface(faces[i], i);
drawarrows();
flushimage(display, 1);
}
void
eresized(int new)
{
lockdisplay(display);
if(new && getwindow(display, Refnone) < 0) {
fprint(2, "can't reattach to window\n");
killall("reattach");
}
resized();
unlockdisplay(display);
}
#if 0
int
getmouse(Mouse *m)
{
int n;
static int eof;
char buf[128];
if(eof)
return 0;
for(;;){
n = read(mousefd, buf, sizeof(buf));
if(n <= 0){
/* so callers needn't check return value every time */
eof = 1;
m->buttons = 0;
return 0;
}
//jpc n = eatomouse(m, buf, n);
if(n > 0)
return 1;
}
}
#endif
int
getmouse(Mouse *m)
{
static int eof;
if(eof)
return 0;
if( readmouse(mousectl) < 0 ) {
eof = 1;
m->buttons = 0;
return 0;
}
else {
*m = mousectl->m;
/* m->buttons = mousectl->m.buttons;
m->xy.x = mousectl->m.xy.x;
m->xy.y = mousectl->m.xy.y;
m->msec = mousectl->m.msec; */
return 1;
}
}
enum
{
Clicksize = 3, /* pixels */
};
int
scroll(int but, Point p)
{
int delta;
delta = 0;
lockdisplay(display);
if(ptinrect(p, leftr) && first>0){
if(but == 2)
delta = -first;
else{
delta = nacross;
if(delta > first)
delta = first;
delta = -delta;
}
}else if(ptinrect(p, rightr) && last<nfaces){
if(but == 2)
delta = (nfaces-nacross*ndown) - first;
else{
delta = nacross;
if(delta > nfaces-last)
delta = nfaces-last;
}
}
first += delta;
last += delta;
unlockdisplay(display);
if(delta)
eresized(0);
return delta;
}
void
click(int button, Mouse *m)
{
Point p;
int i;
p = m->xy;
while(m->buttons == (1<<(button-1)))
getmouse(m);
if(m->buttons)
return;
if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
return;
switch(button){
case 1:
if(scroll(1, p))
break;
if(history){
/* click clears display */
lockdisplay(display);
for(i=0; i<nfaces; i++)
freeface(faces[i]);
free(faces);
faces=nil;
nfaces = 0;
unlockdisplay(display);
eresized(0);
return;
}else{
for(i=first; i<last; i++) /* clear vwhois faces */
if(ptinrect(p, facerect(i-first))
&& strstr(faces[i]->str[Sshow], "/XXXvwhois")){
delface(i);
flushimage(display, 1);
}
}
break;
case 2:
scroll(2, p);
break;
case 3:
scroll(3, p);
lockdisplay(display);
for(i=first; i<last; i++)
if(ptinrect(p, facerect(i-first))){
showmail(faces[i]);
break;
}
unlockdisplay(display);
break;
}
}
void
mouseproc(void *dummy)
{
Mouse mouse;
while(getmouse(&mouse)){
if(mouse.buttons == 1)
click(1, &mouse);
else if(mouse.buttons == 2)
click(2, &mouse);
else if(mouse.buttons == 4)
click(3, &mouse);
while(mouse.buttons)
getmouse(&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");
threadexitsall(s);
}
void
startproc(void (*f)(void), int index)
{
int pid;
switch(pid = rfork(RFPROC|RFNOWAIT)){ //jpc removed |RFMEM
case -1:
fprint(2, "faces: fork failed: %r\n");
killall("fork failed");
case 0:
f();
fprint(2, "faces: %s process exits\n", procnames[index]);
if(index >= 0)
killall("process died");
threadexitsall(nil);
}
if(index >= 0)
pids[index] = pid;
}
void
usage(void)
{
fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n");
threadexitsall("usage");
}
void
threadmain(int argc, char *argv[])
{
int i;
ARGBEGIN{
case 'h':
history++;
break;
case 'i':
initload++;
break;
case 'm':
addmaildir(EARGF(usage()));
maildir = nil;
break;
case 'W':
winsize = EARGF(usage());
break;
default:
usage();
}ARGEND
if(initdraw(nil, nil, "faces") < 0){
fprint(2, "faces: initdraw failed: %r\n");
threadexitsall("initdraw");
}
if(maildir)
addmaildir(maildir);
init();
unlockdisplay(display); /* initdraw leaves it locked */
display->locking = 1; /* tell library we're using the display lock */
setdate();
eresized(0);
pids[Mainp] = getpid();
pids[Timep] = proccreate(timeproc, nil, 16000);
pids[Mousep] = proccreate(mouseproc, nil, 16000);
if(initload)
for(i = 0; i < nmaildirs; i++)
loadmboxfaces(maildirs[i]);
faceproc();
fprint(2, "faces: %s process exits\n", procnames[Mainp]);
killall(nil);
}

26
src/cmd/faces/mkfile Normal file
View file

@ -0,0 +1,26 @@
<$PLAN9/src/mkhdr
# default domain for faces, overridden by $facedom
DEFAULT=\"astro\"
TARG=faces
OFILES=main.$O\
facedb.$O\
plumb.$O\
util.$O\
HFILES=faces.h\
BIN=$PLAN9/bin
UPDATE=\
mkfile\
$HFILES\
${OFILES:%.$O=%.c}\
<$PLAN9/src/mkone
CFLAGS=$CFLAGS '-DDEFAULT='$DEFAULT
$O.dblook: dblook.$O facedb.$O util.$O
$LD -o $target $prereq

398
src/cmd/faces/plumb.c Normal file
View file

@ -0,0 +1,398 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <plumb.h>
#include <regexp.h>
#include <bio.h>
#include <9pclient.h>
#include "faces.h"
static int showfd = -1;
static int seefd = -1;
static int logfd = -1;
static char *user;
static char *logtag;
char **maildirs;
int nmaildirs;
void
initplumb(void)
{
showfd = plumbopen("send", OWRITE);
seefd = plumbopen("seemail", OREAD);
if(seefd < 0){
logfd = open(unsharp("#9/log/mail"), OREAD);
seek(logfd, 0LL, 2);
user = getenv("user");
if(user == nil){
fprint(2, "faces: can't find user name: %r\n");
exits("$user");
}
logtag = emalloc(32+strlen(user)+1);
sprint(logtag, " delivered %s From ", user);
}
}
void
addmaildir(char *dir)
{
maildirs = erealloc(maildirs, (nmaildirs+1)*sizeof(char*));
maildirs[nmaildirs++] = dir;
}
char*
attr(Face *f)
{
static char buf[128];
if(f->str[Sdigest]){
snprint(buf, sizeof buf, "digest=%s", f->str[Sdigest]);
return buf;
}
return nil;
}
void
showmail(Face *f)
{
Plumbmsg pm;
Plumbattr a;
char *s;
if(showfd<0 || f->str[Sshow]==nil || f->str[Sshow][0]=='\0')
return;
s = emalloc(strlen("/mail/fs")+1+strlen(f->str[Sshow]));
sprint(s,"/mail/fs/%s",f->str[Sshow]);
pm.src = "faces";
pm.dst = "showmail";
pm.wdir = "/mail/fs";
pm.type = "text";
a.name = "digest";
a.value = f->str[Sdigest];
a.next = nil;
pm.attr = &a;
pm.ndata = strlen(s);
pm.data = s;
plumbsend(showfd,&pm);
}
char*
value(Plumbattr *attr, char *key, char *def)
{
char *v;
v = plumblookup(attr, key);
if(v)
return v;
return def;
}
void
setname(Face *f, char *sender)
{
char *at, *bang;
char *p;
/* works with UTF-8, although it's written as ASCII */
for(p=sender; *p!='\0'; p++)
*p = tolower(*p);
f->str[Suser] = sender;
at = strchr(sender, '@');
if(at){
*at++ = '\0';
f->str[Sdomain] = estrdup(at);
return;
}
bang = strchr(sender, '!');
if(bang){
*bang++ = '\0';
f->str[Suser] = estrdup(bang);
f->str[Sdomain] = sender;
return;
}
}
int
getc(void)
{
static uchar buf[512];
static int nbuf = 0;
static int i = 0;
while(i == nbuf){
i = 0;
nbuf = read(logfd, buf, sizeof buf);
if(nbuf == 0){
sleep(15000);
continue;
}
if(nbuf < 0)
return -1;
}
return buf[i++];
}
char*
getline(char *buf, int n)
{
int i, c;
for(i=0; i<n-1; i++){
c = getc();
if(c <= 0)
return nil;
if(c == '\n')
break;
buf[i] = c;
}
buf[i] = '\0';
return buf;
}
static char* months[] = {
"jan", "feb", "mar", "apr",
"may", "jun", "jul", "aug",
"sep", "oct", "nov", "dec"
};
static int
getmon(char *s)
{
int i;
for(i=0; i<nelem(months); i++)
if(cistrcmp(months[i], s) == 0)
return i;
return -1;
}
/* Fri Jul 23 14:05:14 EDT 1999 */
ulong
parsedatev(char **a)
{
char *p;
Tm tm;
memset(&tm, 0, sizeof tm);
if((tm.mon=getmon(a[1])) == -1)
goto Err;
tm.mday = strtol(a[2], &p, 10);
if(*p != '\0')
goto Err;
tm.hour = strtol(a[3], &p, 10);
if(*p != ':')
goto Err;
tm.min = strtol(p+1, &p, 10);
if(*p != ':')
goto Err;
tm.sec = strtol(p+1, &p, 10);
if(*p != '\0')
goto Err;
if(strlen(a[4]) != 3)
goto Err;
strcpy(tm.zone, a[4]);
if(strlen(a[5]) != 4)
goto Err;
tm.year = strtol(a[5], &p, 10);
if(*p != '\0')
goto Err;
tm.year -= 1900;
return tm2sec(&tm);
Err:
return time(0);
}
ulong
parsedate(char *s)
{
char *f[10];
int nf;
nf = getfields(s, f, nelem(f), 1, " ");
if(nf < 6)
return time(0);
return parsedatev(f);
}
/* achille Jul 23 14:05:15 delivered jmk From ms.com!bub Fri Jul 23 14:05:14 EDT 1999 (plan9.bell-labs.com!jmk) 1352 */
/* achille Oct 26 13:45:42 remote local!rsc From rsc Sat Oct 26 13:45:41 EDT 2002 (rsc) 170 */
int
parselog(char *s, char **sender, ulong *xtime)
{
char *f[20];
int nf;
nf = getfields(s, f, nelem(f), 1, " ");
if(nf < 14)
return 0;
if(strcmp(f[4], "delivered") == 0 && strcmp(f[5], user) == 0)
goto Found;
if(strcmp(f[4], "remote") == 0 && strncmp(f[5], "local!", 6) == 0 && strcmp(f[5]+6, user) == 0)
goto Found;
return 0;
Found:
*sender = estrdup(f[7]);
*xtime = parsedatev(&f[8]);
return 1;
}
int
logrecv(char **sender, ulong *xtime)
{
char buf[4096];
for(;;){
if(getline(buf, sizeof buf) == nil)
return 0;
if(parselog(buf, sender, xtime))
return 1;
}
return -1;
}
char*
tweakdate(char *d)
{
char e[8];
/* d, date = "Mon Aug 2 23:46:55 EDT 1999" */
if(strlen(d) < strlen("Mon Aug 2 23:46:55 EDT 1999"))
return estrdup("");
if(strncmp(date, d, 4+4+3) == 0)
snprint(e, sizeof e, "%.5s", d+4+4+3); /* 23:46 */
else
snprint(e, sizeof e, "%.6s", d+4); /* Aug 2 */
return estrdup(e);
}
Face*
nextface(void)
{
int i;
Face *f;
Plumbmsg *m;
char *t, *senderp, *showmailp, *digestp;
ulong xtime;
f = emalloc(sizeof(Face));
for(;;){
if(seefd >= 0){
m = plumbrecv(seefd);
if(m == nil)
killall("error on seemail plumb port");
t = value(m->attr, "mailtype", "");
if(strcmp(t, "delete") == 0)
delete(m->data, value(m->attr, "digest", nil));
else if(strcmp(t, "new") != 0)
fprint(2, "faces: unknown plumb message type %s\n", t);
else for(i=0; i<nmaildirs; i++) {
if(strncmp(m->data,"/mail/fs/",strlen("/mail/fs/")) == 0)
m->data += strlen("/mail/fs/");
if(strncmp(m->data, maildirs[i], strlen(maildirs[i])) == 0)
goto Found;
}
plumbfree(m);
continue;
Found:
xtime = parsedate(value(m->attr, "date", date));
digestp = value(m->attr, "digest", nil);
if(alreadyseen(digestp)){
/* duplicate upas/fs can send duplicate messages */
plumbfree(m);
continue;
}
senderp = estrdup(value(m->attr, "sender", "???"));
showmailp = estrdup(m->data);
if(digestp)
digestp = estrdup(digestp);
plumbfree(m);
}else{
if(logrecv(&senderp, &xtime) <= 0)
killall("error reading log file");
showmailp = estrdup("");
digestp = nil;
}
setname(f, senderp);
f->time = xtime;
f->tm = *localtime(xtime);
f->str[Sshow] = showmailp;
f->str[Sdigest] = digestp;
return f;
}
return nil;
}
char*
iline(char *data, char **pp)
{
char *p;
for(p=data; *p!='\0' && *p!='\n'; p++)
;
if(*p == '\n')
*p++ = '\0';
*pp = p;
return data;
}
Face*
dirface(char *dir, char *num)
{
Face *f;
char *from, *date;
char buf[1024], *info, *p, *digest;
int n;
ulong len;
CFid *fid;
#if 0
/*
* loadmbox leaves us in maildir, so we needn't
* walk /mail/fs/mbox for each face; this makes startup
* a fair bit quicker.
*/
if(getwd(pwd, sizeof pwd) != nil && strcmp(pwd, dir) == 0)
sprint(buf, "%s/info", num);
else
sprint(buf, "%s/%s/info", dir, num);
#endif
sprint(buf, "%s/%s/info", dir, num);
len = fsdirlen(upasfs, buf);
if(len <= 0)
return nil;
fid = fsopen(upasfs,buf, OREAD);
if(fid == nil)
return nil;
info = emalloc(len+1);
n = fsreadn(fid, info, len);
fsclose(fid);
if(n < 0){
free(info);
return nil;
}
info[n] = '\0';
f = emalloc(sizeof(Face));
from = iline(info, &p); /* from */
iline(p, &p); /* to */
iline(p, &p); /* cc */
iline(p, &p); /* replyto */
date = iline(p, &p); /* date */
setname(f, estrdup(from));
f->time = parsedate(date);
f->tm = *localtime(f->time);
sprint(buf, "%s/%s", dir, num);
f->str[Sshow] = estrdup(buf);
iline(p, &p); /* subject */
iline(p, &p); /* mime content type */
iline(p, &p); /* mime disposition */
iline(p, &p); /* filename */
digest = iline(p, &p); /* digest */
f->str[Sdigest] = estrdup(digest);
free(info);
return f;
}

42
src/cmd/faces/util.c Normal file
View file

@ -0,0 +1,42 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <plumb.h>
#include <9pclient.h>
#include "faces.h"
void*
emalloc(ulong sz)
{
void *v;
v = malloc(sz);
if(v == nil) {
fprint(2, "out of memory allocating %ld\n", sz);
exits("mem");
}
memset(v, 0, sz);
return v;
}
void*
erealloc(void *v, ulong sz)
{
v = realloc(v, sz);
if(v == nil) {
fprint(2, "out of memory allocating %ld\n", sz);
exits("mem");
}
return v;
}
char*
estrdup(char *s)
{
char *t;
if((t = strdup(s)) == nil) {
fprint(2, "out of memory in strdup(%.10s)\n", s);
exits("mem");
}
return t;
}