Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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