facedb.c 10 KB

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