summaryrefslogtreecommitdiff
path: root/sys/src/cmd/faces/facedb.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/faces/facedb.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/faces/facedb.c')
-rwxr-xr-xsys/src/cmd/faces/facedb.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/sys/src/cmd/faces/facedb.c b/sys/src/cmd/faces/facedb.c
new file mode 100755
index 000000000..8cbb1a79d
--- /dev/null
+++ b/sys/src/cmd/faces/facedb.c
@@ -0,0 +1,578 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <plumb.h>
+#include <regexp.h>
+#include <bio.h>
+#include "faces.h"
+
+enum /* number of deleted faces to cache */
+{
+ Nsave = 20,
+};
+
+static Facefile *facefiles;
+static int nsaved;
+static char *facedom;
+static char *homeface;
+
+/*
+ * 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
+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, char *list)
+{
+ static char buf[200];
+ char *p, *ep, *q, *nextp, *file;
+ char *bbuf, *ebuf;
+ Reprog *exp;
+
+ if(dom == nil || *dom == 0)
+ return nil;
+
+ if(list == nil || (file = readfile(list)) == 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(char *dom, char *user, char *dir, char *dict)
+{
+ static char buf[1024];
+ char *file, *p, *nextp, *q;
+
+ if((file = readfile(dict)) == 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");
+ snprint(buf, sizeof buf, "%s/%s", dir, q);
+ q = buf+strlen(buf);
+ while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
+ *--q = 0;
+ free(file);
+ return estrdup(buf);
+ }
+ }
+ free(file);
+ return nil;
+}
+
+static char*
+estrstrdup(char *a, char *b)
+{
+ char *t;
+
+ t = emalloc(strlen(a)+strlen(b)+1);
+ strcpy(t, a);
+ strcat(t, b);
+ return t;
+}
+
+static char*
+tryfindfiledir(char *dom, char *user, char *dir)
+{
+ char *dict, *ndir, *x;
+ int fd;
+ int i, n;
+ Dir *d;
+
+ /*
+ * If this directory has a .machinelist, use it.
+ */
+ x = estrstrdup(dir, "/.machinelist");
+ dom = estrdup(translatedomain(dom, x));
+ free(x);
+
+ /*
+ * If this directory has a .dict, use it.
+ */
+ dict = estrstrdup(dir, "/.dict");
+ if(access(dict, AEXIST) >= 0){
+ x = tryfindpicture(dom, user, dir, dict);
+ free(dict);
+ free(dom);
+ return x;
+ }
+ free(dict);
+
+ /*
+ * If not, recurse into subdirectories.
+ * Ignore 512x512 directories.
+ * Save 48x48 directories for later.
+ */
+ if((fd = open(dir, OREAD)) < 0)
+ return nil;
+ while((n = dirread(fd, &d)) > 0){
+ for(i=0; i<n; i++){
+ if((d[i].mode&DMDIR)
+ && strncmp(d[i].name, "512x", 4) != 0
+ && strncmp(d[i].name, "48x48x", 6) != 0){
+ ndir = emalloc(strlen(dir)+1+strlen(d[i].name)+1);
+ strcpy(ndir, dir);
+ strcat(ndir, "/");
+ strcat(ndir, d[i].name);
+ if((x = tryfindfiledir(dom, user, ndir)) != nil){
+ free(ndir);
+ free(d);
+ close(fd);
+ free(dom);
+ return x;
+ }
+ }
+ }
+ free(d);
+ }
+ close(fd);
+
+ /*
+ * Handle 48x48 directories in the right order.
+ */
+ ndir = estrstrdup(dir, "/48x48x8");
+ for(i=8; i>0; i>>=1){
+ ndir[strlen(ndir)-1] = i+'0';
+ if(access(ndir, AEXIST) >= 0 && (x = tryfindfiledir(dom, user, ndir)) != nil){
+ free(ndir);
+ free(dom);
+ return x;
+ }
+ }
+ free(ndir);
+ free(dom);
+ return nil;
+}
+
+static char*
+tryfindfile(char *dom, char *user)
+{
+ char *p;
+
+ while(dom && *dom){
+ if(homeface && (p = tryfindfiledir(dom, user, homeface)) != nil)
+ return p;
+ if((p = tryfindfiledir(dom, user, "/lib/face")) != nil)
+ return p;
+ if((dom = strchr(dom, '.')) == nil)
+ break;
+ dom++;
+ }
+ return nil;
+}
+
+char*
+findfile(Face *f, char *dom, char *user)
+{
+ char *p;
+
+ if(facedom == nil){
+ facedom = getenv("facedom");
+ if(facedom == nil)
+ facedom = DEFAULT;
+ }
+ if(dom == nil)
+ dom = facedom;
+ if(homeface == nil)
+ homeface = smprint("%s/lib/face", getenv("home"));
+
+ f->unknown = 0;
+ if((p = tryfindfile(dom, user)) != nil)
+ return p;
+ f->unknown = 1;
+ p = tryfindfile(dom, "unknown");
+ if(p != nil || strcmp(dom, facedom) == 0)
+ return p;
+ return tryfindfile("unknown", "unknown");
+}
+
+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;
+ }
+}