xref: /plan9/sys/src/cmd/faces/facedb.c (revision 25910e17a84eba80a977b719060afe3fd7c77113)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <draw.h>
47dd7cddfSDavid du Colombier #include <plumb.h>
57dd7cddfSDavid du Colombier #include <regexp.h>
67dd7cddfSDavid du Colombier #include <bio.h>
77dd7cddfSDavid du Colombier #include "faces.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier enum	/* number of deleted faces to cache */
107dd7cddfSDavid du Colombier {
117dd7cddfSDavid du Colombier 	Nsave	= 20,
127dd7cddfSDavid du Colombier };
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier static Facefile	*facefiles;
157dd7cddfSDavid du Colombier static int		nsaved;
167dd7cddfSDavid du Colombier static char	*facedom;
17aa1525d4SDavid du Colombier static char *homeface;
187dd7cddfSDavid du Colombier 
197dd7cddfSDavid du Colombier /*
207dd7cddfSDavid du Colombier  * Loading the files is slow enough on a dial-up line to be worth this trouble
217dd7cddfSDavid du Colombier  */
227dd7cddfSDavid du Colombier typedef struct Readcache	Readcache;
237dd7cddfSDavid du Colombier struct Readcache {
247dd7cddfSDavid du Colombier 	char *file;
257dd7cddfSDavid du Colombier 	char *data;
267dd7cddfSDavid du Colombier 	long mtime;
277dd7cddfSDavid du Colombier 	long rdtime;
287dd7cddfSDavid du Colombier 	Readcache *next;
297dd7cddfSDavid du Colombier };
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier static Readcache *rcache;
327dd7cddfSDavid du Colombier 
339a747e4fSDavid du Colombier ulong
dirlen(char * s)349a747e4fSDavid du Colombier dirlen(char *s)
359a747e4fSDavid du Colombier {
369a747e4fSDavid du Colombier 	Dir *d;
379a747e4fSDavid du Colombier 	ulong len;
389a747e4fSDavid du Colombier 
399a747e4fSDavid du Colombier 	d = dirstat(s);
409a747e4fSDavid du Colombier 	if(d == nil)
419a747e4fSDavid du Colombier 		return 0;
429a747e4fSDavid du Colombier 	len = d->length;
439a747e4fSDavid du Colombier 	free(d);
449a747e4fSDavid du Colombier 	return len;
459a747e4fSDavid du Colombier }
469a747e4fSDavid du Colombier 
479a747e4fSDavid du Colombier ulong
dirmtime(char * s)489a747e4fSDavid du Colombier dirmtime(char *s)
499a747e4fSDavid du Colombier {
509a747e4fSDavid du Colombier 	Dir *d;
519a747e4fSDavid du Colombier 	ulong t;
529a747e4fSDavid du Colombier 
539a747e4fSDavid du Colombier 	d = dirstat(s);
549a747e4fSDavid du Colombier 	if(d == nil)
559a747e4fSDavid du Colombier 		return 0;
569a747e4fSDavid du Colombier 	t = d->mtime;
579a747e4fSDavid du Colombier 	free(d);
589a747e4fSDavid du Colombier 	return t;
599a747e4fSDavid du Colombier }
609a747e4fSDavid du Colombier 
617dd7cddfSDavid du Colombier static char*
doreadfile(char * s)627dd7cddfSDavid du Colombier doreadfile(char *s)
637dd7cddfSDavid du Colombier {
647dd7cddfSDavid du Colombier 	char *p;
657dd7cddfSDavid du Colombier 	int fd, n;
669a747e4fSDavid du Colombier 	ulong len;
677dd7cddfSDavid du Colombier 
689a747e4fSDavid du Colombier 	len = dirlen(s);
699a747e4fSDavid du Colombier 	if(len == 0)
707dd7cddfSDavid du Colombier 		return nil;
717dd7cddfSDavid du Colombier 
729a747e4fSDavid du Colombier 	p = malloc(len+1);
737dd7cddfSDavid du Colombier 	if(p == nil)
747dd7cddfSDavid du Colombier 		return nil;
757dd7cddfSDavid du Colombier 
767dd7cddfSDavid du Colombier 	if((fd = open(s, OREAD)) < 0
779a747e4fSDavid du Colombier 	|| (n = readn(fd, p, len)) < 0) {
785d459b5aSDavid du Colombier 		close(fd);
797dd7cddfSDavid du Colombier 		free(p);
807dd7cddfSDavid du Colombier 		return nil;
817dd7cddfSDavid du Colombier 	}
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier 	p[n] = '\0';
847dd7cddfSDavid du Colombier 	return p;
857dd7cddfSDavid du Colombier }
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier static char*
readfile(char * s)887dd7cddfSDavid du Colombier readfile(char *s)
897dd7cddfSDavid du Colombier {
907dd7cddfSDavid du Colombier 	Readcache *r, **l;
917dd7cddfSDavid du Colombier 	char *p;
929a747e4fSDavid du Colombier 	ulong mtime;
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier 	for(l=&rcache, r=*l; r; l=&r->next, r=*l) {
957dd7cddfSDavid du Colombier 		if(strcmp(r->file, s) != 0)
967dd7cddfSDavid du Colombier 			continue;
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier 		/*
997dd7cddfSDavid du Colombier 		 * if it's less than 30 seconds since we read it, or it
1007dd7cddfSDavid du Colombier 		 * hasn't changed, send back our copy
1017dd7cddfSDavid du Colombier 		 */
1027dd7cddfSDavid du Colombier 		if(time(0) - r->rdtime < 30)
1037dd7cddfSDavid du Colombier 			return strdup(r->data);
1049a747e4fSDavid du Colombier 		if(dirmtime(s) == r->mtime) {
1057dd7cddfSDavid du Colombier 			r->rdtime = time(0);
1067dd7cddfSDavid du Colombier 			return strdup(r->data);
1077dd7cddfSDavid du Colombier 		}
1087dd7cddfSDavid du Colombier 
1097dd7cddfSDavid du Colombier 		/* out of date, remove this and fall out of loop */
1107dd7cddfSDavid du Colombier 		*l = r->next;
1117dd7cddfSDavid du Colombier 		free(r->file);
1127dd7cddfSDavid du Colombier 		free(r->data);
1137dd7cddfSDavid du Colombier 		free(r);
1147dd7cddfSDavid du Colombier 		break;
1157dd7cddfSDavid du Colombier 	}
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	/* add to cache */
1189a747e4fSDavid du Colombier 	mtime = dirmtime(s);
1199a747e4fSDavid du Colombier 	if(mtime == 0)
1207dd7cddfSDavid du Colombier 		return nil;
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier 	if((p = doreadfile(s)) == nil)
1237dd7cddfSDavid du Colombier 		return nil;
1247dd7cddfSDavid du Colombier 
1257dd7cddfSDavid du Colombier 	r = malloc(sizeof(*r));
1267dd7cddfSDavid du Colombier 	if(r == nil)
1277dd7cddfSDavid du Colombier 		return nil;
1289a747e4fSDavid du Colombier 	r->mtime = mtime;
1297dd7cddfSDavid du Colombier 	r->file = estrdup(s);
1307dd7cddfSDavid du Colombier 	r->data = p;
1317dd7cddfSDavid du Colombier 	r->rdtime = time(0);
1327dd7cddfSDavid du Colombier 	r->next = rcache;
1337dd7cddfSDavid du Colombier 	rcache = r;
1347dd7cddfSDavid du Colombier 	return strdup(r->data);
1357dd7cddfSDavid du Colombier }
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier static char*
translatedomain(char * dom,char * list)138aa1525d4SDavid du Colombier translatedomain(char *dom, char *list)
1397dd7cddfSDavid du Colombier {
1407dd7cddfSDavid du Colombier 	static char buf[200];
1417dd7cddfSDavid du Colombier 	char *p, *ep, *q, *nextp, *file;
1427dd7cddfSDavid du Colombier 	char *bbuf, *ebuf;
1437dd7cddfSDavid du Colombier 	Reprog *exp;
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	if(dom == nil || *dom == 0)
1467dd7cddfSDavid du Colombier 		return nil;
1477dd7cddfSDavid du Colombier 
148aa1525d4SDavid du Colombier 	if(list == nil || (file = readfile(list)) == nil)
1497dd7cddfSDavid du Colombier 		return dom;
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	for(p=file; p; p=nextp) {
1527dd7cddfSDavid du Colombier 		if(nextp = strchr(p, '\n'))
1537dd7cddfSDavid du Colombier 			*nextp++ = '\0';
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier 		if(*p == '#' || (q = strpbrk(p, " \t")) == nil || q-p > sizeof(buf)-2)
1567dd7cddfSDavid du Colombier 			continue;
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 		bbuf = buf+1;
1597dd7cddfSDavid du Colombier 		ebuf = buf+(1+(q-p));
1607dd7cddfSDavid du Colombier 		strncpy(bbuf, p, ebuf-bbuf);
1617dd7cddfSDavid du Colombier 		*ebuf = 0;
1627dd7cddfSDavid du Colombier 		if(*bbuf != '^')
1637dd7cddfSDavid du Colombier 			*--bbuf = '^';
1647dd7cddfSDavid du Colombier 		if(ebuf[-1] != '$') {
1657dd7cddfSDavid du Colombier 			*ebuf++ = '$';
1667dd7cddfSDavid du Colombier 			*ebuf = 0;
1677dd7cddfSDavid du Colombier 		}
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 		if((exp = regcomp(bbuf)) == nil){
1707dd7cddfSDavid du Colombier 			fprint(2, "bad regexp in machinelist: %s\n", bbuf);
1717dd7cddfSDavid du Colombier 			killall("regexp");
1727dd7cddfSDavid du Colombier 		}
1737dd7cddfSDavid du Colombier 
1747dd7cddfSDavid du Colombier 		if(regexec(exp, dom, 0, 0)){
1757dd7cddfSDavid du Colombier 			free(exp);
1767dd7cddfSDavid du Colombier 			ep = p+strlen(p);
1777dd7cddfSDavid du Colombier 			q += strspn(q, " \t");
1787dd7cddfSDavid du Colombier 			if(ep-q+2 > sizeof buf) {
17959cc4ca5SDavid du Colombier 				fprint(2, "huge replacement in machinelist: %.*s\n", utfnlen(q, ep-q), q);
1807dd7cddfSDavid du Colombier 				exits("bad big replacement");
1817dd7cddfSDavid du Colombier 			}
1827dd7cddfSDavid du Colombier 			strncpy(buf, q, ep-q);
1837dd7cddfSDavid du Colombier 			ebuf = buf+(ep-q);
1847dd7cddfSDavid du Colombier 			*ebuf = 0;
1857dd7cddfSDavid du Colombier 			while(ebuf > buf && (ebuf[-1] == ' ' || ebuf[-1] == '\t'))
1867dd7cddfSDavid du Colombier 				*--ebuf = 0;
1877dd7cddfSDavid du Colombier 			free(file);
1887dd7cddfSDavid du Colombier 			return buf;
1897dd7cddfSDavid du Colombier 		}
1907dd7cddfSDavid du Colombier 		free(exp);
1917dd7cddfSDavid du Colombier 	}
1927dd7cddfSDavid du Colombier 	free(file);
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier 	return dom;
1957dd7cddfSDavid du Colombier }
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier static char*
tryfindpicture(char * dom,char * user,char * dir,char * dict)198aa1525d4SDavid du Colombier tryfindpicture(char *dom, char *user, char *dir, char *dict)
199c1ba0ed0SDavid du Colombier {
200aa1525d4SDavid du Colombier 	static char buf[1024];
201aa1525d4SDavid du Colombier 	char *file, *p, *nextp, *q;
202c1ba0ed0SDavid du Colombier 
203aa1525d4SDavid du Colombier 	if((file = readfile(dict)) == nil)
204c1ba0ed0SDavid du Colombier 		return nil;
205c1ba0ed0SDavid du Colombier 
206c1ba0ed0SDavid du Colombier 	snprint(buf, sizeof buf, "%s/%s", dom, user);
207c1ba0ed0SDavid du Colombier 
208c1ba0ed0SDavid du Colombier 	for(p=file; p; p=nextp){
209c1ba0ed0SDavid du Colombier 		if(nextp = strchr(p, '\n'))
210c1ba0ed0SDavid du Colombier 			*nextp++ = '\0';
211c1ba0ed0SDavid du Colombier 
212c1ba0ed0SDavid du Colombier 		if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
213c1ba0ed0SDavid du Colombier 			continue;
214c1ba0ed0SDavid du Colombier 		*q++ = 0;
215c1ba0ed0SDavid du Colombier 
216c1ba0ed0SDavid du Colombier 		if(strcmp(buf, p) == 0){
217c1ba0ed0SDavid du Colombier 			q += strspn(q, " \t");
218aa1525d4SDavid du Colombier 			snprint(buf, sizeof buf, "%s/%s", dir, q);
219aa1525d4SDavid du Colombier 			q = buf+strlen(buf);
220c1ba0ed0SDavid du Colombier 			while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
221c1ba0ed0SDavid du Colombier 				*--q = 0;
222c1ba0ed0SDavid du Colombier 			free(file);
223aa1525d4SDavid du Colombier 			return estrdup(buf);
224c1ba0ed0SDavid du Colombier 		}
225c1ba0ed0SDavid du Colombier 	}
226c1ba0ed0SDavid du Colombier 	free(file);
227c1ba0ed0SDavid du Colombier 	return nil;
228c1ba0ed0SDavid du Colombier }
229c1ba0ed0SDavid du Colombier 
230c1ba0ed0SDavid du Colombier static char*
estrstrdup(char * a,char * b)231aa1525d4SDavid du Colombier estrstrdup(char *a, char *b)
2327dd7cddfSDavid du Colombier {
233aa1525d4SDavid du Colombier 	char *t;
2347dd7cddfSDavid du Colombier 
235aa1525d4SDavid du Colombier 	t = emalloc(strlen(a)+strlen(b)+1);
236aa1525d4SDavid du Colombier 	strcpy(t, a);
237aa1525d4SDavid du Colombier 	strcat(t, b);
238aa1525d4SDavid du Colombier 	return t;
239aa1525d4SDavid du Colombier }
240aa1525d4SDavid du Colombier 
241aa1525d4SDavid du Colombier static char*
tryfindfiledir(char * dom,char * user,char * dir)242aa1525d4SDavid du Colombier tryfindfiledir(char *dom, char *user, char *dir)
243aa1525d4SDavid du Colombier {
244aa1525d4SDavid du Colombier 	char *dict, *ndir, *x;
245aa1525d4SDavid du Colombier 	int fd;
246aa1525d4SDavid du Colombier 	int i, n;
247aa1525d4SDavid du Colombier 	Dir *d;
248aa1525d4SDavid du Colombier 
249aa1525d4SDavid du Colombier 	/*
250aa1525d4SDavid du Colombier 	 * If this directory has a .machinelist, use it.
251aa1525d4SDavid du Colombier 	 */
252aa1525d4SDavid du Colombier 	x = estrstrdup(dir, "/.machinelist");
253aa1525d4SDavid du Colombier 	dom = estrdup(translatedomain(dom, x));
254aa1525d4SDavid du Colombier 	free(x);
255aa1525d4SDavid du Colombier 
256aa1525d4SDavid du Colombier 	/*
257aa1525d4SDavid du Colombier 	 * If this directory has a .dict, use it.
258aa1525d4SDavid du Colombier 	 */
259aa1525d4SDavid du Colombier 	dict = estrstrdup(dir, "/.dict");
260aa1525d4SDavid du Colombier 	if(access(dict, AEXIST) >= 0){
261aa1525d4SDavid du Colombier 		x = tryfindpicture(dom, user, dir, dict);
262aa1525d4SDavid du Colombier 		free(dict);
263aa1525d4SDavid du Colombier 		free(dom);
264aa1525d4SDavid du Colombier 		return x;
265aa1525d4SDavid du Colombier 	}
266aa1525d4SDavid du Colombier 	free(dict);
267aa1525d4SDavid du Colombier 
268aa1525d4SDavid du Colombier 	/*
269aa1525d4SDavid du Colombier 	 * If not, recurse into subdirectories.
270aa1525d4SDavid du Colombier 	 * Ignore 512x512 directories.
271aa1525d4SDavid du Colombier 	 * Save 48x48 directories for later.
272aa1525d4SDavid du Colombier 	 */
273aa1525d4SDavid du Colombier 	if((fd = open(dir, OREAD)) < 0)
2747dd7cddfSDavid du Colombier 		return nil;
275aa1525d4SDavid du Colombier 	while((n = dirread(fd, &d)) > 0){
276aa1525d4SDavid du Colombier 		for(i=0; i<n; i++){
277aa1525d4SDavid du Colombier 			if((d[i].mode&DMDIR)
278*25910e17SDavid du Colombier 			&& strncmp(d[i].name, "512x", 4) != 0
279aa1525d4SDavid du Colombier 			&& strncmp(d[i].name, "48x48x", 6) != 0){
280aa1525d4SDavid du Colombier 				ndir = emalloc(strlen(dir)+1+strlen(d[i].name)+1);
281aa1525d4SDavid du Colombier 				strcpy(ndir, dir);
282aa1525d4SDavid du Colombier 				strcat(ndir, "/");
283aa1525d4SDavid du Colombier 				strcat(ndir, d[i].name);
284aa1525d4SDavid du Colombier 				if((x = tryfindfiledir(dom, user, ndir)) != nil){
285aa1525d4SDavid du Colombier 					free(ndir);
286aa1525d4SDavid du Colombier 					free(d);
287aa1525d4SDavid du Colombier 					close(fd);
288aa1525d4SDavid du Colombier 					free(dom);
289aa1525d4SDavid du Colombier 					return x;
2907dd7cddfSDavid du Colombier 				}
2917dd7cddfSDavid du Colombier 			}
292aa1525d4SDavid du Colombier 		}
293aa1525d4SDavid du Colombier 		free(d);
294aa1525d4SDavid du Colombier 	}
295aa1525d4SDavid du Colombier 	close(fd);
296aa1525d4SDavid du Colombier 
297aa1525d4SDavid du Colombier 	/*
298aa1525d4SDavid du Colombier 	 * Handle 48x48 directories in the right order.
299aa1525d4SDavid du Colombier 	 */
300aa1525d4SDavid du Colombier 	ndir = estrstrdup(dir, "/48x48x8");
301aa1525d4SDavid du Colombier 	for(i=8; i>0; i>>=1){
302aa1525d4SDavid du Colombier 		ndir[strlen(ndir)-1] = i+'0';
303aa1525d4SDavid du Colombier 		if(access(ndir, AEXIST) >= 0 && (x = tryfindfiledir(dom, user, ndir)) != nil){
304aa1525d4SDavid du Colombier 			free(ndir);
305aa1525d4SDavid du Colombier 			free(dom);
306aa1525d4SDavid du Colombier 			return x;
307aa1525d4SDavid du Colombier 		}
308aa1525d4SDavid du Colombier 	}
309aa1525d4SDavid du Colombier 	free(ndir);
310aa1525d4SDavid du Colombier 	free(dom);
3117dd7cddfSDavid du Colombier 	return nil;
3127dd7cddfSDavid du Colombier }
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier static char*
tryfindfile(char * dom,char * user)315aa1525d4SDavid du Colombier tryfindfile(char *dom, char *user)
316c1ba0ed0SDavid du Colombier {
317aa1525d4SDavid du Colombier 	char *p;
318c1ba0ed0SDavid du Colombier 
319aa1525d4SDavid du Colombier 	while(dom && *dom){
320aa1525d4SDavid du Colombier 		if(homeface && (p = tryfindfiledir(dom, user, homeface)) != nil)
321aa1525d4SDavid du Colombier 			return p;
322aa1525d4SDavid du Colombier 		if((p = tryfindfiledir(dom, user, "/lib/face")) != nil)
323aa1525d4SDavid du Colombier 			return p;
324aa1525d4SDavid du Colombier 		if((dom = strchr(dom, '.')) == nil)
3257dd7cddfSDavid du Colombier 			break;
326aa1525d4SDavid du Colombier 		dom++;
3277dd7cddfSDavid du Colombier 	}
3287dd7cddfSDavid du Colombier 	return nil;
3297dd7cddfSDavid du Colombier }
3307dd7cddfSDavid du Colombier 
3317dd7cddfSDavid du Colombier char*
findfile(Face * f,char * dom,char * user)3327dd7cddfSDavid du Colombier findfile(Face *f, char *dom, char *user)
3337dd7cddfSDavid du Colombier {
3347dd7cddfSDavid du Colombier 	char *p;
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 	if(facedom == nil){
3377dd7cddfSDavid du Colombier 		facedom = getenv("facedom");
3387dd7cddfSDavid du Colombier 		if(facedom == nil)
3397dd7cddfSDavid du Colombier 			facedom = DEFAULT;
3407dd7cddfSDavid du Colombier 	}
3417dd7cddfSDavid du Colombier 	if(dom == nil)
3427dd7cddfSDavid du Colombier 		dom = facedom;
343aa1525d4SDavid du Colombier 	if(homeface == nil)
344aa1525d4SDavid du Colombier 		homeface = smprint("%s/lib/face", getenv("home"));
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier 	f->unknown = 0;
347aa1525d4SDavid du Colombier 	if((p = tryfindfile(dom, user)) != nil)
3487dd7cddfSDavid du Colombier 		return p;
3497dd7cddfSDavid du Colombier 	f->unknown = 1;
350aa1525d4SDavid du Colombier 	p = tryfindfile(dom, "unknown");
3517dd7cddfSDavid du Colombier 	if(p != nil || strcmp(dom, facedom) == 0)
3527dd7cddfSDavid du Colombier 		return p;
353aa1525d4SDavid du Colombier 	return tryfindfile("unknown", "unknown");
3547dd7cddfSDavid du Colombier }
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier static
3577dd7cddfSDavid du Colombier void
clearsaved(void)3587dd7cddfSDavid du Colombier clearsaved(void)
3597dd7cddfSDavid du Colombier {
3607dd7cddfSDavid du Colombier 	Facefile *f, *next, **lf;
3617dd7cddfSDavid du Colombier 
3627dd7cddfSDavid du Colombier 	lf = &facefiles;
3637dd7cddfSDavid du Colombier 	for(f=facefiles; f!=nil; f=next){
3647dd7cddfSDavid du Colombier 		next = f->next;
3657dd7cddfSDavid du Colombier 		if(f->ref > 0){
3667dd7cddfSDavid du Colombier 			*lf = f;
3677dd7cddfSDavid du Colombier 			lf = &(f->next);
3687dd7cddfSDavid du Colombier 			continue;
3697dd7cddfSDavid du Colombier 		}
3707dd7cddfSDavid du Colombier 		if(f->image != display->black && f->image != display->white)
3717dd7cddfSDavid du Colombier 			freeimage(f->image);
3727dd7cddfSDavid du Colombier 		free(f->file);
3737dd7cddfSDavid du Colombier 		free(f);
3747dd7cddfSDavid du Colombier 	}
3757dd7cddfSDavid du Colombier 	*lf = nil;
3767dd7cddfSDavid du Colombier 	nsaved = 0;
3777dd7cddfSDavid du Colombier }
3787dd7cddfSDavid du Colombier 
3797dd7cddfSDavid du Colombier void
freefacefile(Facefile * f)3807dd7cddfSDavid du Colombier freefacefile(Facefile *f)
3817dd7cddfSDavid du Colombier {
3827dd7cddfSDavid du Colombier 	if(f==nil || f->ref-->1)
3837dd7cddfSDavid du Colombier 		return;
3847dd7cddfSDavid du Colombier 	if(++nsaved > Nsave)
3857dd7cddfSDavid du Colombier 		clearsaved();
3867dd7cddfSDavid du Colombier }
3877dd7cddfSDavid du Colombier 
3887dd7cddfSDavid du Colombier static Image*
myallocimage(ulong chan)3897dd7cddfSDavid du Colombier myallocimage(ulong chan)
3907dd7cddfSDavid du Colombier {
3917dd7cddfSDavid du Colombier 	Image *img;
3927dd7cddfSDavid du Colombier 	img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
3937dd7cddfSDavid du Colombier 	if(img == nil){
3947dd7cddfSDavid du Colombier 		clearsaved();
3957dd7cddfSDavid du Colombier 		img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
3967dd7cddfSDavid du Colombier 		if(img == nil)
3977dd7cddfSDavid du Colombier 			return nil;
3987dd7cddfSDavid du Colombier 	}
3997dd7cddfSDavid du Colombier 	return img;
4007dd7cddfSDavid du Colombier }
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier 
4037dd7cddfSDavid du Colombier static Image*
readbit(int fd,ulong chan)4047dd7cddfSDavid du Colombier readbit(int fd, ulong chan)
4057dd7cddfSDavid du Colombier {
4067dd7cddfSDavid du Colombier 	char buf[4096], hx[4], *p;
4077dd7cddfSDavid du Colombier 	uchar data[Facesize*Facesize];	/* more than enough */
4087dd7cddfSDavid du Colombier 	int nhx, i, n, ndata, nbit;
4097dd7cddfSDavid du Colombier 	Image *img;
4107dd7cddfSDavid du Colombier 
4117dd7cddfSDavid du Colombier 	n = readn(fd, buf, sizeof buf);
4127dd7cddfSDavid du Colombier 	if(n <= 0)
4137dd7cddfSDavid du Colombier 		return nil;
4147dd7cddfSDavid du Colombier 	if(n >= sizeof buf)
4157dd7cddfSDavid du Colombier 		n = sizeof(buf)-1;
4167dd7cddfSDavid du Colombier 	buf[n] = '\0';
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier 	n = 0;
4197dd7cddfSDavid du Colombier 	nhx = 0;
4207dd7cddfSDavid du Colombier 	nbit = chantodepth(chan);
4217dd7cddfSDavid du Colombier 	ndata = (Facesize*Facesize*nbit)/8;
4227dd7cddfSDavid du Colombier 	p = buf;
4237dd7cddfSDavid du Colombier 	while(n < ndata) {
4247dd7cddfSDavid du Colombier 		p = strpbrk(p+1, "0123456789abcdefABCDEF");
4257dd7cddfSDavid du Colombier 		if(p == nil)
4267dd7cddfSDavid du Colombier 			break;
4277dd7cddfSDavid du Colombier 		if(p[0] == '0' && p[1] == 'x')
4287dd7cddfSDavid du Colombier 			continue;
4297dd7cddfSDavid du Colombier 
4307dd7cddfSDavid du Colombier 		hx[nhx] = *p;
4317dd7cddfSDavid du Colombier 		if(++nhx == 2) {
4327dd7cddfSDavid du Colombier 			hx[nhx] = 0;
4337dd7cddfSDavid du Colombier 			i = strtoul(hx, 0, 16);
4347dd7cddfSDavid du Colombier 			data[n++] = i;
4357dd7cddfSDavid du Colombier 			nhx = 0;
4367dd7cddfSDavid du Colombier 		}
4377dd7cddfSDavid du Colombier 	}
4387dd7cddfSDavid du Colombier 	if(n < ndata)
4397dd7cddfSDavid du Colombier 		return allocimage(display, Rect(0,0,Facesize,Facesize), CMAP8, 0, 0x88888888);
4407dd7cddfSDavid du Colombier 
4417dd7cddfSDavid du Colombier 	img = myallocimage(chan);
4427dd7cddfSDavid du Colombier 	if(img == nil)
4437dd7cddfSDavid du Colombier 		return nil;
4447dd7cddfSDavid du Colombier 	loadimage(img, img->r, data, ndata);
4457dd7cddfSDavid du Colombier 	return img;
4467dd7cddfSDavid du Colombier }
4477dd7cddfSDavid du Colombier 
4487dd7cddfSDavid du Colombier static Facefile*
readface(char * fn)4497dd7cddfSDavid du Colombier readface(char *fn)
4507dd7cddfSDavid du Colombier {
4517dd7cddfSDavid du Colombier 	int x, y, fd;
4527dd7cddfSDavid du Colombier 	uchar bits;
4537dd7cddfSDavid du Colombier 	uchar *p;
4547dd7cddfSDavid du Colombier 	Image *mask;
4557dd7cddfSDavid du Colombier 	Image *face;
4567dd7cddfSDavid du Colombier 	char buf[16];
4577dd7cddfSDavid du Colombier 	uchar data[Facesize*Facesize];
4587dd7cddfSDavid du Colombier 	uchar mdata[(Facesize*Facesize)/8];
4597dd7cddfSDavid du Colombier 	Facefile *f;
4609a747e4fSDavid du Colombier 	Dir *d;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	for(f=facefiles; f!=nil; f=f->next){
4637dd7cddfSDavid du Colombier 		if(strcmp(fn, f->file) == 0){
4647dd7cddfSDavid du Colombier 			if(f->image == nil)
4657dd7cddfSDavid du Colombier 				break;
4667dd7cddfSDavid du Colombier 			if(time(0) - f->rdtime >= 30) {
4679a747e4fSDavid du Colombier 				if(dirmtime(fn) != f->mtime){
4687dd7cddfSDavid du Colombier 					f = nil;
4697dd7cddfSDavid du Colombier 					break;
4707dd7cddfSDavid du Colombier 				}
4717dd7cddfSDavid du Colombier 				f->rdtime = time(0);
4727dd7cddfSDavid du Colombier 			}
4737dd7cddfSDavid du Colombier 			f->ref++;
4747dd7cddfSDavid du Colombier 			return f;
4757dd7cddfSDavid du Colombier 		}
4767dd7cddfSDavid du Colombier 	}
4777dd7cddfSDavid du Colombier 
4787dd7cddfSDavid du Colombier 	if((fd = open(fn, OREAD)) < 0)
4797dd7cddfSDavid du Colombier 		return nil;
4807dd7cddfSDavid du Colombier 
4817dd7cddfSDavid du Colombier 	if(readn(fd, buf, sizeof buf) != sizeof buf){
4827dd7cddfSDavid du Colombier 		close(fd);
4837dd7cddfSDavid du Colombier 		return nil;
4847dd7cddfSDavid du Colombier 	}
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	mask = nil;
4897dd7cddfSDavid du Colombier 	if(buf[0] == '0' && buf[1] == 'x'){
4907dd7cddfSDavid du Colombier 		/* greyscale faces are just masks that we draw black through! */
4917dd7cddfSDavid du Colombier 		if(buf[2+8] == ',')	/* ldepth 1 */
4927dd7cddfSDavid du Colombier 			mask = readbit(fd, GREY2);
4937dd7cddfSDavid du Colombier 		else
4947dd7cddfSDavid du Colombier 			mask = readbit(fd, GREY1);
4957dd7cddfSDavid du Colombier 		face = display->black;
4967dd7cddfSDavid du Colombier 	}else{
4977dd7cddfSDavid du Colombier 		face = readimage(display, fd, 0);
4987dd7cddfSDavid du Colombier 		if(face == nil)
4997dd7cddfSDavid du Colombier 			goto Done;
5007dd7cddfSDavid du Colombier 		else if(face->chan == GREY4 || face->chan == GREY8){	/* greyscale: use inversion as mask */
5017dd7cddfSDavid du Colombier 			mask = myallocimage(face->chan);
5027dd7cddfSDavid du Colombier 			/* okay if mask is nil: that will copy the image white background and all */
5037dd7cddfSDavid du Colombier 			if(mask == nil)
5047dd7cddfSDavid du Colombier 				goto Done;
5057dd7cddfSDavid du Colombier 
5067dd7cddfSDavid du Colombier 			/* invert greyscale image */
5077dd7cddfSDavid du Colombier 			draw(mask, mask->r, display->white, nil, ZP);
5087dd7cddfSDavid du Colombier 			gendraw(mask, mask->r, display->black, ZP, face, face->r.min);
5097dd7cddfSDavid du Colombier 			freeimage(face);
5107dd7cddfSDavid du Colombier 			face = display->black;
5117dd7cddfSDavid du Colombier 		}else if(face->depth == 8){	/* snarf the bytes back and do a fill. */
5127dd7cddfSDavid du Colombier 			mask = myallocimage(GREY1);
5137dd7cddfSDavid du Colombier 			if(mask == nil)
5147dd7cddfSDavid du Colombier 				goto Done;
5157dd7cddfSDavid du Colombier 			if(unloadimage(face, face->r, data, Facesize*Facesize) != Facesize*Facesize){
5167dd7cddfSDavid du Colombier 				freeimage(mask);
5177dd7cddfSDavid du Colombier 				goto Done;
5187dd7cddfSDavid du Colombier 			}
5197dd7cddfSDavid du Colombier 			bits = 0;
5207dd7cddfSDavid du Colombier 			p = mdata;
5217dd7cddfSDavid du Colombier 			for(y=0; y<Facesize; y++){
5227dd7cddfSDavid du Colombier 				for(x=0; x<Facesize; x++){
5237dd7cddfSDavid du Colombier 					bits <<= 1;
5247dd7cddfSDavid du Colombier 					if(data[Facesize*y+x] != 0xFF)
5257dd7cddfSDavid du Colombier 						bits |= 1;
5267dd7cddfSDavid du Colombier 					if((x&7) == 7)
5277dd7cddfSDavid du Colombier 						*p++ = bits&0xFF;
5287dd7cddfSDavid du Colombier 				}
5297dd7cddfSDavid du Colombier 			}
5307dd7cddfSDavid du Colombier 			if(loadimage(mask, mask->r, mdata, sizeof mdata) != sizeof mdata){
5317dd7cddfSDavid du Colombier 				freeimage(mask);
5327dd7cddfSDavid du Colombier 				goto Done;
5337dd7cddfSDavid du Colombier 			}
5347dd7cddfSDavid du Colombier 		}
5357dd7cddfSDavid du Colombier 	}
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier Done:
5387dd7cddfSDavid du Colombier 	/* always add at beginning of list, so updated files don't collide in cache */
5397dd7cddfSDavid du Colombier 	if(f == nil){
5407dd7cddfSDavid du Colombier 		f = emalloc(sizeof(Facefile));
5417dd7cddfSDavid du Colombier 		f->file = estrdup(fn);
5429a747e4fSDavid du Colombier 		d = dirfstat(fd);
5439a747e4fSDavid du Colombier 		if(d != nil){
5449a747e4fSDavid du Colombier 			f->mtime = d->mtime;
5459a747e4fSDavid du Colombier 			free(d);
5469a747e4fSDavid du Colombier 		}
5477dd7cddfSDavid du Colombier 		f->next = facefiles;
5487dd7cddfSDavid du Colombier 		facefiles = f;
5497dd7cddfSDavid du Colombier 	}
5507dd7cddfSDavid du Colombier 	f->ref++;
5517dd7cddfSDavid du Colombier 	f->image = face;
5527dd7cddfSDavid du Colombier 	f->mask = mask;
5537dd7cddfSDavid du Colombier 	f->rdtime = time(0);
5547dd7cddfSDavid du Colombier 	close(fd);
5557dd7cddfSDavid du Colombier 	return f;
5567dd7cddfSDavid du Colombier }
5577dd7cddfSDavid du Colombier 
5587dd7cddfSDavid du Colombier void
findbit(Face * f)5597dd7cddfSDavid du Colombier findbit(Face *f)
5607dd7cddfSDavid du Colombier {
5617dd7cddfSDavid du Colombier 	char *fn;
5627dd7cddfSDavid du Colombier 
5637dd7cddfSDavid du Colombier 	fn = findfile(f, f->str[Sdomain], f->str[Suser]);
5647dd7cddfSDavid du Colombier 	if(fn) {
5657dd7cddfSDavid du Colombier 		if(strstr(fn, "unknown"))
5667dd7cddfSDavid du Colombier 			f->unknown = 1;
5677dd7cddfSDavid du Colombier 		f->file = readface(fn);
5687dd7cddfSDavid du Colombier 	}
5697dd7cddfSDavid du Colombier 	if(f->file){
5707dd7cddfSDavid du Colombier 		f->bit = f->file->image;
5717dd7cddfSDavid du Colombier 		f->mask = f->file->mask;
5727dd7cddfSDavid du Colombier 	}else{
5737dd7cddfSDavid du Colombier 		/* if returns nil, this is still ok: draw(nil) works */
5747dd7cddfSDavid du Colombier 		f->bit = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
5757dd7cddfSDavid du Colombier 		replclipr(f->bit, 1, Rect(0, 0, Facesize, Facesize));
5767dd7cddfSDavid du Colombier 		f->mask = nil;
5777dd7cddfSDavid du Colombier 	}
5787dd7cddfSDavid du Colombier }
579