mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-12 11:10:07 +00:00
initial faces (John Cummings)
This commit is contained in:
parent
663ddde9d0
commit
b330c942b4
7 changed files with 1909 additions and 0 deletions
30
src/cmd/faces/dblook.c
Normal file
30
src/cmd/faces/dblook.c
Normal 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
562
src/cmd/faces/facedb.c
Normal 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
68
src/cmd/faces/faces.h
Normal 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
783
src/cmd/faces/main.c
Normal 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
26
src/cmd/faces/mkfile
Normal 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
398
src/cmd/faces/plumb.c
Normal 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
42
src/cmd/faces/util.c
Normal 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;
|
||||
}
|
||||
|
Loading…
Reference in a new issue