9 enum /* number of deleted faces to cache */
14 static Facefile *facefiles;
17 static char *homeface;
20 * Loading the files is slow enough on a dial-up line to be worth this trouble
22 typedef struct Readcache Readcache;
31 static Readcache *rcache;
76 if((fd = open(s, OREAD)) < 0
77 || (n = readn(fd, p, len)) < 0) {
94 for(l=&rcache, r=*l; r; l=&r->next, r=*l) {
95 if(strcmp(r->file, s) != 0)
99 * if it's less than 30 seconds since we read it, or it
100 * hasn't changed, send back our copy
102 if(time(0) - r->rdtime < 30)
103 return strdup(r->data);
104 if(dirmtime(s) == r->mtime) {
106 return strdup(r->data);
109 /* out of date, remove this and fall out of loop */
122 if((p = doreadfile(s)) == nil)
125 r = malloc(sizeof(*r));
129 r->file = estrdup(s);
134 return strdup(r->data);
138 translatedomain(char *dom, char *list)
140 static char buf[200];
141 char *p, *ep, *q, *nextp, *file;
145 if(dom == nil || *dom == 0)
148 if(list == nil || (file = readfile(list)) == nil)
151 for(p=file; p; p=nextp) {
152 if(nextp = strchr(p, '\n'))
155 if(*p == '#' || (q = strpbrk(p, " \t")) == nil || q-p > sizeof(buf)-2)
159 ebuf = buf+(1+(q-p));
160 strncpy(bbuf, p, ebuf-bbuf);
164 if(ebuf[-1] != '$') {
169 if((exp = regcomp(bbuf)) == nil){
170 fprint(2, "bad regexp in machinelist: %s\n", bbuf);
174 if(regexec(exp, dom, 0, 0)){
177 q += strspn(q, " \t");
178 if(ep-q+2 > sizeof buf) {
179 fprint(2, "huge replacement in machinelist: %.*s\n", utfnlen(q, ep-q), q);
180 exits("bad big replacement");
182 strncpy(buf, q, ep-q);
185 while(ebuf > buf && (ebuf[-1] == ' ' || ebuf[-1] == '\t'))
198 tryfindpicture(char *dom, char *user, char *dir, char *dict)
200 static char buf[1024];
201 char *file, *p, *nextp, *q;
203 if((file = readfile(dict)) == nil)
206 snprint(buf, sizeof buf, "%s/%s", dom, user);
208 for(p=file; p; p=nextp){
209 if(nextp = strchr(p, '\n'))
212 if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
216 if(strcmp(buf, p) == 0){
217 q += strspn(q, " \t");
218 snprint(buf, sizeof buf, "%s/%s", dir, q);
220 while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
231 estrstrdup(char *a, char *b)
235 t = emalloc(strlen(a)+strlen(b)+1);
242 tryfindfiledir(char *dom, char *user, char *dir)
244 char *dict, *ndir, *x;
250 * If this directory has a .machinelist, use it.
252 x = estrstrdup(dir, "/.machinelist");
253 dom = estrdup(translatedomain(dom, x));
257 * If this directory has a .dict, use it.
259 dict = estrstrdup(dir, "/.dict");
260 if(access(dict, AEXIST) >= 0){
261 x = tryfindpicture(dom, user, dir, dict);
269 * If not, recurse into subdirectories.
270 * Ignore 512x512 directories.
271 * Save 48x48 directories for later.
273 if((fd = open(dir, OREAD)) < 0)
275 while((n = dirread(fd, &d)) > 0){
278 && strncmp(d[i].name, "512x", 4) != 0
279 && strncmp(d[i].name, "48x48x", 6) != 0){
280 ndir = emalloc(strlen(dir)+1+strlen(d[i].name)+1);
283 strcat(ndir, d[i].name);
284 if((x = tryfindfiledir(dom, user, ndir)) != nil){
298 * Handle 48x48 directories in the right order.
300 ndir = estrstrdup(dir, "/48x48x8");
301 for(i=8; i>0; i>>=1){
302 ndir[strlen(ndir)-1] = i+'0';
303 if(access(ndir, AEXIST) >= 0 && (x = tryfindfiledir(dom, user, ndir)) != nil){
315 tryfindfile(char *dom, char *user)
320 if(homeface && (p = tryfindfiledir(dom, user, homeface)) != nil)
322 if((p = tryfindfiledir(dom, user, "/lib/face")) != nil)
324 if((dom = strchr(dom, '.')) == nil)
332 findfile(Face *f, char *dom, char *user)
337 facedom = getenv("facedom");
344 homeface = smprint("%s/lib/face", getenv("home"));
347 if((p = tryfindfile(dom, user)) != nil)
350 p = tryfindfile(dom, "unknown");
351 if(p != nil || strcmp(dom, facedom) == 0)
353 return tryfindfile("unknown", "unknown");
360 Facefile *f, *next, **lf;
363 for(f=facefiles; f!=nil; f=next){
370 if(f->image != display->black && f->image != display->white)
380 freefacefile(Facefile *f)
382 if(f==nil || f->ref-->1)
389 myallocimage(ulong chan)
392 img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
395 img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
404 readbit(int fd, ulong chan)
406 char buf[4096], hx[4], *p;
407 uchar data[Facesize*Facesize]; /* more than enough */
408 int nhx, i, n, ndata, nbit;
411 n = readn(fd, buf, sizeof buf);
420 nbit = chantodepth(chan);
421 ndata = (Facesize*Facesize*nbit)/8;
424 p = strpbrk(p+1, "0123456789abcdefABCDEF");
427 if(p[0] == '0' && p[1] == 'x')
433 i = strtoul(hx, 0, 16);
439 return allocimage(display, Rect(0,0,Facesize,Facesize), CMAP8, 0, 0x88888888);
441 img = myallocimage(chan);
444 loadimage(img, img->r, data, ndata);
457 uchar data[Facesize*Facesize];
458 uchar mdata[(Facesize*Facesize)/8];
462 for(f=facefiles; f!=nil; f=f->next){
463 if(strcmp(fn, f->file) == 0){
466 if(time(0) - f->rdtime >= 30) {
467 if(dirmtime(fn) != f->mtime){
478 if((fd = open(fn, OREAD)) < 0)
481 if(readn(fd, buf, sizeof buf) != sizeof buf){
489 if(buf[0] == '0' && buf[1] == 'x'){
490 /* greyscale faces are just masks that we draw black through! */
491 if(buf[2+8] == ',') /* ldepth 1 */
492 mask = readbit(fd, GREY2);
494 mask = readbit(fd, GREY1);
495 face = display->black;
497 face = readimage(display, fd, 0);
500 else if(face->chan == GREY4 || face->chan == GREY8){ /* greyscale: use inversion as mask */
501 mask = myallocimage(face->chan);
502 /* okay if mask is nil: that will copy the image white background and all */
506 /* invert greyscale image */
507 draw(mask, mask->r, display->white, nil, ZP);
508 gendraw(mask, mask->r, display->black, ZP, face, face->r.min);
510 face = display->black;
511 }else if(face->depth == 8){ /* snarf the bytes back and do a fill. */
512 mask = myallocimage(GREY1);
515 if(unloadimage(face, face->r, data, Facesize*Facesize) != Facesize*Facesize){
521 for(y=0; y<Facesize; y++){
522 for(x=0; x<Facesize; x++){
524 if(data[Facesize*y+x] != 0xFF)
530 if(loadimage(mask, mask->r, mdata, sizeof mdata) != sizeof mdata){
538 /* always add at beginning of list, so updated files don't collide in cache */
540 f = emalloc(sizeof(Facefile));
541 f->file = estrdup(fn);
563 fn = findfile(f, f->str[Sdomain], f->str[Suser]);
565 if(strstr(fn, "unknown"))
567 f->file = readface(fn);
570 f->bit = f->file->image;
571 f->mask = f->file->mask;
573 /* if returns nil, this is still ok: draw(nil) works */
574 f->bit = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
575 replclipr(f->bit, 1, Rect(0, 0, Facesize, Facesize));