URI:
       tfacedb.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfacedb.c (10715B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <draw.h>
            4 #include <plumb.h>
            5 #include <regexp.h>
            6 #include <bio.h>
            7 #include <9pclient.h>
            8 #include "faces.h"
            9 
           10 enum        /* number of deleted faces to cache */
           11 {
           12         Nsave        = 20
           13 };
           14 
           15 static Facefile        *facefiles;
           16 static int                nsaved;
           17 static char        *facedom;
           18 static char        *libface;
           19 static char        *homeface;
           20 
           21 /*
           22  * Loading the files is slow enough on a dial-up line to be worth this trouble
           23  */
           24 typedef struct Readcache        Readcache;
           25 struct Readcache {
           26         char *file;
           27         char *data;
           28         long mtime;
           29         long rdtime;
           30         Readcache *next;
           31 };
           32 
           33 static Readcache *rcache;
           34 
           35 ulong
           36 dirlen(char *s)
           37 {
           38         Dir *d;
           39         ulong len;
           40 
           41         d = dirstat(s);
           42         if(d == nil)
           43                 return 0;
           44         len = d->length;
           45         free(d);
           46         return len;
           47 }
           48 
           49 ulong
           50 fsdirlen(CFsys *fs,char *s)
           51 {
           52         Dir *d;
           53         ulong len;
           54 
           55         d = fsdirstat(fs,s);
           56         if(d == nil)
           57                 return 0;
           58         len = d->length;
           59         free(d);
           60         return len;
           61 }
           62 
           63 ulong
           64 dirmtime(char *s)
           65 {
           66         Dir *d;
           67         ulong t;
           68 
           69         d = dirstat(s);
           70         if(d == nil)
           71                 return 0;
           72         t = d->mtime;
           73         free(d);
           74         return t;
           75 }
           76 
           77 static char*
           78 doreadfile(char *s)
           79 {
           80         char *p;
           81         int fd, n;
           82         ulong len;
           83 
           84         len = dirlen(s);
           85         if(len == 0)
           86                 return nil;
           87 
           88         p = malloc(len+1);
           89         if(p == nil)
           90                 return nil;
           91 
           92         if((fd = open(s, OREAD)) < 0
           93         || (n = readn(fd, p, len)) < 0) {
           94                 close(fd);
           95                 free(p);
           96                 return nil;
           97         }
           98 
           99         p[n] = '\0';
          100         return p;
          101 }
          102 
          103 static char*
          104 readfile(char *s)
          105 {
          106         Readcache *r, **l;
          107         char *p;
          108         ulong mtime;
          109 
          110         for(l=&rcache, r=*l; r; l=&r->next, r=*l) {
          111                 if(strcmp(r->file, s) != 0)
          112                         continue;
          113 
          114                 /*
          115                  * if it's less than 30 seconds since we read it, or it
          116                  * hasn't changed, send back our copy
          117                  */
          118                 if(time(0) - r->rdtime < 30)
          119                         return strdup(r->data);
          120                 if(dirmtime(s) == r->mtime) {
          121                         r->rdtime = time(0);
          122                         return strdup(r->data);
          123                 }
          124 
          125                 /* out of date, remove this and fall out of loop */
          126                 *l = r->next;
          127                 free(r->file);
          128                 free(r->data);
          129                 free(r);
          130                 break;
          131         }
          132 
          133         /* add to cache */
          134         mtime = dirmtime(s);
          135         if(mtime == 0)
          136                 return nil;
          137 
          138         if((p = doreadfile(s)) == nil)
          139                 return nil;
          140 
          141         r = malloc(sizeof(*r));
          142         if(r == nil)
          143                 return nil;
          144         r->mtime = mtime;
          145         r->file = estrdup(s);
          146         r->data = p;
          147         r->rdtime = time(0);
          148         r->next = rcache;
          149         rcache = r;
          150         return strdup(r->data);
          151 }
          152 
          153 static char*
          154 translatedomain(char *dom, char *list)
          155 {
          156         static char buf[200];
          157         char *p, *ep, *q, *nextp, *file;
          158         char *bbuf, *ebuf;
          159         Reprog *exp;
          160 
          161         if(dom == nil || *dom == 0)
          162                 return nil;
          163 
          164         if(list == nil || (file = readfile(list)) == nil)
          165                 return dom;
          166 
          167         for(p=file; p; p=nextp) {
          168                 if(nextp = strchr(p, '\n'))
          169                         *nextp++ = '\0';
          170 
          171                 if(*p == '#' || (q = strpbrk(p, " \t")) == nil || q-p > sizeof(buf)-2)
          172                         continue;
          173 
          174                 bbuf = buf+1;
          175                 ebuf = buf+(1+(q-p));
          176                 strncpy(bbuf, p, ebuf-bbuf);
          177                 *ebuf = 0;
          178                 if(*bbuf != '^')
          179                         *--bbuf = '^';
          180                 if(ebuf[-1] != '$') {
          181                         *ebuf++ = '$';
          182                         *ebuf = 0;
          183                 }
          184 
          185                 if((exp = regcomp(bbuf)) == nil){
          186                         fprint(2, "bad regexp in machinelist: %s\n", bbuf);
          187                         killall("regexp");
          188                 }
          189 
          190                 if(regexec(exp, dom, 0, 0)){
          191                         free(exp);
          192                         ep = p+strlen(p);
          193                         q += strspn(q, " \t");
          194                         if(ep-q+2 > sizeof buf) {
          195                                 fprint(2, "huge replacement in machinelist: %.*s\n", utfnlen(q, ep-q), q);
          196                                 exits("bad big replacement");
          197                         }
          198                         strncpy(buf, q, ep-q);
          199                         ebuf = buf+(ep-q);
          200                         *ebuf = 0;
          201                         while(ebuf > buf && (ebuf[-1] == ' ' || ebuf[-1] == '\t'))
          202                                 *--ebuf = 0;
          203                         free(file);
          204                         return buf;
          205                 }
          206                 free(exp);
          207         }
          208         free(file);
          209 
          210         return dom;
          211 }
          212 
          213 static char*
          214 tryfindpicture(char *dom, char *user, char *dir, char *dict)
          215 {
          216         static char buf[1024];
          217         char *file, *p, *nextp, *q;
          218 
          219         if((file = readfile(dict)) == nil)
          220                 return nil;
          221 
          222         snprint(buf, sizeof buf, "%s/%s", dom, user);
          223 
          224         for(p=file; p; p=nextp){
          225                 if(nextp = strchr(p, '\n'))
          226                         *nextp++ = '\0';
          227 
          228                 if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
          229                         continue;
          230                 *q++ = 0;
          231 
          232                 if(strcmp(buf, p) == 0){
          233                         q += strspn(q, " \t");
          234                         snprint(buf, sizeof buf, "%s/%s", dir, q);
          235                         q = buf+strlen(buf);
          236                         while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
          237                                 *--q = 0;
          238                         free(file);
          239                         return estrdup(buf);
          240                 }
          241         }
          242         free(file);
          243         return nil;
          244 }
          245 
          246 static char*
          247 estrstrdup(char *a, char *b)
          248 {
          249         char *t;
          250 
          251         t = emalloc(strlen(a)+strlen(b)+1);
          252         strcpy(t, a);
          253         strcat(t, b);
          254         return t;
          255 }
          256 
          257 static char*
          258 tryfindfiledir(char *dom, char *user, char *dir)
          259 {
          260         char *dict, *ndir, *x, *odom;
          261         int fd;
          262         int i, n;
          263         Dir *d;
          264 
          265         /*
          266          * If this directory has a .machinelist, use it.
          267          */
          268         x = estrstrdup(dir, "/.machinelist");
          269         dom = estrdup(translatedomain(dom, x));
          270         free(x);
          271         /*
          272          * If this directory has a .dict, use it.
          273          */
          274         dict = estrstrdup(dir, "/.dict");
          275         if(access(dict, AEXIST) >= 0){
          276                 x = tryfindpicture(dom, user, dir, dict);
          277                 free(dict);
          278                 free(dom);
          279                 return x;
          280         }
          281         free(dict);
          282 
          283         /*
          284          * If not, recurse into subdirectories.
          285          * Ignore 48x48xN directories for now.
          286          */
          287         if((fd = open(dir, OREAD)) < 0)
          288                 return nil;
          289         while((n = dirread(fd, &d)) > 0){
          290                 for(i=0; i<n; i++){
          291                         if((d[i].mode&DMDIR)&& strncmp(d[i].name, "48x48x", 6) != 0){
          292                                 ndir = emalloc(strlen(dir)+1+strlen(d[i].name)+1);
          293                                 strcpy(ndir, dir);
          294                                 strcat(ndir, "/");
          295                                 strcat(ndir, d[i].name);
          296                                 if((x = tryfindfiledir(dom, user, ndir)) != nil){
          297                                         free(ndir);
          298                                         free(d);
          299                                         close(fd);
          300                                         free(dom);
          301                                         return x;
          302                                 }
          303                         }
          304                 }
          305                 free(d);
          306         }
          307         close(fd);
          308 
          309         /*
          310          * Handle 48x48xN directories in the right order.
          311          */
          312         ndir = estrstrdup(dir, "/48x48x8");
          313         for(i=8; i>0; i>>=1){
          314                 ndir[strlen(ndir)-1] = i+'0';
          315                 if(access(ndir, AEXIST) >= 0 && (x = tryfindfiledir(dom, user, ndir)) != nil){
          316                         free(ndir);
          317                         free(dom);
          318                         return x;
          319                 }
          320         }
          321         free(ndir);
          322         free(dom);
          323         return nil;
          324 }
          325 
          326 static char*
          327 tryfindfile(char *dom, char *user)
          328 {
          329         char *p;
          330 
          331         while(dom && *dom){
          332                 if(homeface && (p = tryfindfiledir(dom, user, homeface)) != nil)
          333                         return p;
          334                 if((p = tryfindfiledir(dom, user, libface)) != nil)
          335                         return p;
          336                 if((dom = strchr(dom, '.')) == nil)
          337                         break;
          338                 dom++;
          339         }
          340         return nil;
          341 }
          342 
          343 char*
          344 findfile(Face *f, char *dom, char *user)
          345 {
          346         char *p;
          347 
          348         if(facedom == nil){
          349                 facedom = getenv("facedom");
          350                 if(facedom == nil)
          351                         facedom = DEFAULT;
          352         }
          353         if(libface == nil)
          354                 libface = unsharp("#9/face");
          355         if(homeface == nil)
          356                 homeface = smprint("%s/lib/face", getenv("HOME"));
          357 
          358         if(dom == nil)
          359                 dom = facedom;
          360 
          361         f->unknown = 0;
          362         if((p = tryfindfile(dom, user)) != nil)
          363                 return p;
          364         f->unknown = 1;
          365         p = tryfindfile(dom, "unknown");
          366         if(p != nil || strcmp(dom, facedom) == 0)
          367                 return p;
          368         return tryfindfile("unknown", "unknown");
          369 }
          370 
          371 static
          372 void
          373 clearsaved(void)
          374 {
          375         Facefile *f, *next, **lf;
          376 
          377         lf = &facefiles;
          378         for(f=facefiles; f!=nil; f=next){
          379                 next = f->next;
          380                 if(f->ref > 0){
          381                         *lf = f;
          382                         lf = &(f->next);
          383                         continue;
          384                 }
          385                 if(f->image != display->black && f->image != display->white)
          386                         freeimage(f->image);
          387                 free(f->file);
          388                 free(f);
          389         }
          390         *lf = nil;
          391         nsaved = 0;
          392 }
          393 
          394 void
          395 freefacefile(Facefile *f)
          396 {
          397         if(f==nil || f->ref-->1)
          398                 return;
          399         if(++nsaved > Nsave)
          400                 clearsaved();
          401 }
          402 
          403 static Image*
          404 myallocimage(ulong chan)
          405 {
          406         Image *img;
          407         img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
          408         if(img == nil){
          409                 clearsaved();
          410                 img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
          411                 if(img == nil)
          412                         return nil;
          413         }
          414         return img;
          415 }
          416 
          417 
          418 static Image*
          419 readbit(int fd, ulong chan)
          420 {
          421         char buf[4096], hx[4], *p;
          422         uchar data[Facesize*Facesize];        /* more than enough */
          423         int nhx, i, n, ndata, nbit;
          424         Image *img;
          425 
          426         n = readn(fd, buf, sizeof buf);
          427         if(n <= 0)
          428                 return nil;
          429         if(n >= sizeof buf)
          430                 n = sizeof(buf)-1;
          431         buf[n] = '\0';
          432 
          433         n = 0;
          434         nhx = 0;
          435         nbit = chantodepth(chan);
          436         ndata = (Facesize*Facesize*nbit)/8;
          437         p = buf;
          438         while(n < ndata) {
          439                 p = strpbrk(p+1, "0123456789abcdefABCDEF");
          440                 if(p == nil)
          441                         break;
          442                 if(p[0] == '0' && p[1] == 'x')
          443                         continue;
          444 
          445                 hx[nhx] = *p;
          446                 if(++nhx == 2) {
          447                         hx[nhx] = 0;
          448                         i = strtoul(hx, 0, 16);
          449                         data[n++] = i;
          450                         nhx = 0;
          451                 }
          452         }
          453         if(n < ndata)
          454                 return allocimage(display, Rect(0,0,Facesize,Facesize), CMAP8, 0, 0x88888888);
          455 
          456         img = myallocimage(chan);
          457         if(img == nil)
          458                 return nil;
          459         loadimage(img, img->r, data, ndata);
          460         return img;
          461 }
          462 
          463 static Facefile*
          464 readface(char *fn)
          465 {
          466         int x, y, fd;
          467         uchar bits;
          468         uchar *p;
          469         Image *mask;
          470         Image *face;
          471         char buf[16];
          472         uchar data[Facesize*Facesize];
          473         uchar mdata[(Facesize*Facesize)/8];
          474         Facefile *f;
          475         Dir *d;
          476 
          477         for(f=facefiles; f!=nil; f=f->next){
          478                 if(strcmp(fn, f->file) == 0){
          479                         if(f->image == nil)
          480                                 break;
          481                         if(time(0) - f->rdtime >= 30) {
          482                                 if(dirmtime(fn) != f->mtime){
          483                                         f = nil;
          484                                         break;
          485                                 }
          486                                 f->rdtime = time(0);
          487                         }
          488                         f->ref++;
          489                         return f;
          490                 }
          491         }
          492 
          493         if((fd = open(fn, OREAD)) < 0)
          494                 return nil;
          495 
          496         if(readn(fd, buf, sizeof buf) != sizeof buf){
          497                 close(fd);
          498                 return nil;
          499         }
          500 
          501         seek(fd, 0, 0);
          502 
          503         mask = nil;
          504         if(buf[0] == '0' && buf[1] == 'x'){
          505                 /* greyscale faces are just masks that we draw black through! */
          506                 if(buf[2+8] == ',')        /* ldepth 1 */
          507                         mask = readbit(fd, GREY2);
          508                 else
          509                         mask = readbit(fd, GREY1);
          510                 face = display->black;
          511         }else{
          512                 face = readimage(display, fd, 0);
          513                 if(face == nil)
          514                         goto Done;
          515                 else if(face->chan == GREY4 || face->chan == GREY8){        /* greyscale: use inversion as mask */
          516                         mask = myallocimage(face->chan);
          517                         /* okay if mask is nil: that will copy the image white background and all */
          518                         if(mask == nil)
          519                                 goto Done;
          520 
          521                         /* invert greyscale image */
          522                         draw(mask, mask->r, display->white, nil, ZP);
          523                         gendraw(mask, mask->r, display->black, ZP, face, face->r.min);
          524                         freeimage(face);
          525                         face = display->black;
          526                 }else if(face->depth == 8){        /* snarf the bytes back and do a fill. */
          527                         mask = myallocimage(GREY1);
          528                         if(mask == nil)
          529                                 goto Done;
          530                         if(unloadimage(face, face->r, data, Facesize*Facesize) != Facesize*Facesize){
          531                                 freeimage(mask);
          532                                 goto Done;
          533                         }
          534                         bits = 0;
          535                         p = mdata;
          536                         for(y=0; y<Facesize; y++){
          537                                 for(x=0; x<Facesize; x++){
          538                                         bits <<= 1;
          539                                         if(data[Facesize*y+x] != 0xFF)
          540                                                 bits |= 1;
          541                                         if((x&7) == 7)
          542                                                 *p++ = bits&0xFF;
          543                                 }
          544                         }
          545                         if(loadimage(mask, mask->r, mdata, sizeof mdata) != sizeof mdata){
          546                                 freeimage(mask);
          547                                 goto Done;
          548                         }
          549                 }
          550         }
          551 
          552 Done:
          553         /* always add at beginning of list, so updated files don't collide in cache */
          554         if(f == nil){
          555                 f = emalloc(sizeof(Facefile));
          556                 f->file = estrdup(fn);
          557                 d = dirfstat(fd);
          558                 if(d != nil){
          559                         f->mtime = d->mtime;
          560                         free(d);
          561                 }
          562                 f->next = facefiles;
          563                 facefiles = f;
          564         }
          565         f->ref++;
          566         f->image = face;
          567         f->mask = mask;
          568         f->rdtime = time(0);
          569         close(fd);
          570         return f;
          571 }
          572 
          573 void
          574 findbit(Face *f)
          575 {
          576         char *fn;
          577 
          578         fn = findfile(f, f->str[Sdomain], f->str[Suser]);
          579         if(fn) {
          580                 if(strstr(fn, "unknown"))
          581                         f->unknown = 1;
          582                 f->file = readface(fn);
          583         }
          584         if(f->file){
          585                 f->bit = f->file->image;
          586                 f->mask = f->file->mask;
          587         }else{
          588                 /* if returns nil, this is still ok: draw(nil) works */
          589                 f->bit = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
          590                 replclipr(f->bit, 1, Rect(0, 0, Facesize, Facesize));
          591                 f->mask = nil;
          592         }
          593 }