facedb.c 9.7 KB

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