diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /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-x | sys/src/cmd/faces/facedb.c | 578 |
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; + } +} |