facedb.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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(char *dom, char *user, int depth)
  168. {
  169. static char buf[200];
  170. char *p, *q, *nextp, *file;
  171. sprint(buf, "/lib/face/48x48x%d/.dict", depth);
  172. if((file = readfile(buf)) == 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. q = buf+snprint(buf, sizeof buf, "/lib/face/48x48x%d/%s", depth, q);
  184. while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
  185. *--q = 0;
  186. free(file);
  187. return buf;
  188. }
  189. }
  190. free(file);
  191. return nil;
  192. }
  193. static char*
  194. tryfindfile(char *dom, char *user, int depth)
  195. {
  196. char *p, *q;
  197. for(;;){
  198. for(p=dom; p; (p=strchr(p, '.')) && p++)
  199. if(q = tryfindpicture(p, user, depth))
  200. return q;
  201. depth >>= 1;
  202. if(depth == 0)
  203. break;
  204. }
  205. return nil;
  206. }
  207. char*
  208. findfile(Face *f, char *dom, char *user)
  209. {
  210. char *p;
  211. int depth;
  212. if(facedom == nil){
  213. facedom = getenv("facedom");
  214. if(facedom == nil)
  215. facedom = DEFAULT;
  216. }
  217. dom = translatedomain(dom);
  218. if(dom == nil)
  219. dom = facedom;
  220. if(screen == nil)
  221. depth = 8;
  222. else
  223. depth = screen->depth;
  224. if(depth > 8)
  225. depth = 8;
  226. f->unknown = 0;
  227. if(p = tryfindfile(dom, user, depth))
  228. return p;
  229. f->unknown = 1;
  230. p = tryfindfile(dom, "unknown", depth);
  231. if(p != nil || strcmp(dom, facedom)==0)
  232. return p;
  233. return tryfindfile("unknown", "unknown", depth);
  234. }
  235. static
  236. void
  237. clearsaved(void)
  238. {
  239. Facefile *f, *next, **lf;
  240. lf = &facefiles;
  241. for(f=facefiles; f!=nil; f=next){
  242. next = f->next;
  243. if(f->ref > 0){
  244. *lf = f;
  245. lf = &(f->next);
  246. continue;
  247. }
  248. if(f->image != display->black && f->image != display->white)
  249. freeimage(f->image);
  250. free(f->file);
  251. free(f);
  252. }
  253. *lf = nil;
  254. nsaved = 0;
  255. }
  256. void
  257. freefacefile(Facefile *f)
  258. {
  259. if(f==nil || f->ref-->1)
  260. return;
  261. if(++nsaved > Nsave)
  262. clearsaved();
  263. }
  264. static Image*
  265. myallocimage(ulong chan)
  266. {
  267. Image *img;
  268. img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
  269. if(img == nil){
  270. clearsaved();
  271. img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, DNofill);
  272. if(img == nil)
  273. return nil;
  274. }
  275. return img;
  276. }
  277. static Image*
  278. readbit(int fd, ulong chan)
  279. {
  280. char buf[4096], hx[4], *p;
  281. uchar data[Facesize*Facesize]; /* more than enough */
  282. int nhx, i, n, ndata, nbit;
  283. Image *img;
  284. n = readn(fd, buf, sizeof buf);
  285. if(n <= 0)
  286. return nil;
  287. if(n >= sizeof buf)
  288. n = sizeof(buf)-1;
  289. buf[n] = '\0';
  290. n = 0;
  291. nhx = 0;
  292. nbit = chantodepth(chan);
  293. ndata = (Facesize*Facesize*nbit)/8;
  294. p = buf;
  295. while(n < ndata) {
  296. p = strpbrk(p+1, "0123456789abcdefABCDEF");
  297. if(p == nil)
  298. break;
  299. if(p[0] == '0' && p[1] == 'x')
  300. continue;
  301. hx[nhx] = *p;
  302. if(++nhx == 2) {
  303. hx[nhx] = 0;
  304. i = strtoul(hx, 0, 16);
  305. data[n++] = i;
  306. nhx = 0;
  307. }
  308. }
  309. if(n < ndata)
  310. return allocimage(display, Rect(0,0,Facesize,Facesize), CMAP8, 0, 0x88888888);
  311. img = myallocimage(chan);
  312. if(img == nil)
  313. return nil;
  314. loadimage(img, img->r, data, ndata);
  315. return img;
  316. }
  317. static Facefile*
  318. readface(char *fn)
  319. {
  320. int x, y, fd;
  321. uchar bits;
  322. uchar *p;
  323. Image *mask;
  324. Image *face;
  325. char buf[16];
  326. uchar data[Facesize*Facesize];
  327. uchar mdata[(Facesize*Facesize)/8];
  328. Facefile *f;
  329. Dir *d;
  330. for(f=facefiles; f!=nil; f=f->next){
  331. if(strcmp(fn, f->file) == 0){
  332. if(f->image == nil)
  333. break;
  334. if(time(0) - f->rdtime >= 30) {
  335. if(dirmtime(fn) != f->mtime){
  336. f = nil;
  337. break;
  338. }
  339. f->rdtime = time(0);
  340. }
  341. f->ref++;
  342. return f;
  343. }
  344. }
  345. if((fd = open(fn, OREAD)) < 0)
  346. return nil;
  347. if(readn(fd, buf, sizeof buf) != sizeof buf){
  348. close(fd);
  349. return nil;
  350. }
  351. seek(fd, 0, 0);
  352. mask = nil;
  353. if(buf[0] == '0' && buf[1] == 'x'){
  354. /* greyscale faces are just masks that we draw black through! */
  355. if(buf[2+8] == ',') /* ldepth 1 */
  356. mask = readbit(fd, GREY2);
  357. else
  358. mask = readbit(fd, GREY1);
  359. face = display->black;
  360. }else{
  361. face = readimage(display, fd, 0);
  362. if(face == nil)
  363. goto Done;
  364. else if(face->chan == GREY4 || face->chan == GREY8){ /* greyscale: use inversion as mask */
  365. mask = myallocimage(face->chan);
  366. /* okay if mask is nil: that will copy the image white background and all */
  367. if(mask == nil)
  368. goto Done;
  369. /* invert greyscale image */
  370. draw(mask, mask->r, display->white, nil, ZP);
  371. gendraw(mask, mask->r, display->black, ZP, face, face->r.min);
  372. freeimage(face);
  373. face = display->black;
  374. }else if(face->depth == 8){ /* snarf the bytes back and do a fill. */
  375. mask = myallocimage(GREY1);
  376. if(mask == nil)
  377. goto Done;
  378. if(unloadimage(face, face->r, data, Facesize*Facesize) != Facesize*Facesize){
  379. freeimage(mask);
  380. goto Done;
  381. }
  382. bits = 0;
  383. p = mdata;
  384. for(y=0; y<Facesize; y++){
  385. for(x=0; x<Facesize; x++){
  386. bits <<= 1;
  387. if(data[Facesize*y+x] != 0xFF)
  388. bits |= 1;
  389. if((x&7) == 7)
  390. *p++ = bits&0xFF;
  391. }
  392. }
  393. if(loadimage(mask, mask->r, mdata, sizeof mdata) != sizeof mdata){
  394. freeimage(mask);
  395. goto Done;
  396. }
  397. }
  398. }
  399. Done:
  400. /* always add at beginning of list, so updated files don't collide in cache */
  401. if(f == nil){
  402. f = emalloc(sizeof(Facefile));
  403. f->file = estrdup(fn);
  404. d = dirfstat(fd);
  405. if(d != nil){
  406. f->mtime = d->mtime;
  407. free(d);
  408. }
  409. f->next = facefiles;
  410. facefiles = f;
  411. }
  412. f->ref++;
  413. f->image = face;
  414. f->mask = mask;
  415. f->rdtime = time(0);
  416. close(fd);
  417. return f;
  418. }
  419. void
  420. findbit(Face *f)
  421. {
  422. char *fn;
  423. fn = findfile(f, f->str[Sdomain], f->str[Suser]);
  424. if(fn) {
  425. if(strstr(fn, "unknown"))
  426. f->unknown = 1;
  427. f->file = readface(fn);
  428. }
  429. if(f->file){
  430. f->bit = f->file->image;
  431. f->mask = f->file->mask;
  432. }else{
  433. /* if returns nil, this is still ok: draw(nil) works */
  434. f->bit = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
  435. replclipr(f->bit, 1, Rect(0, 0, Facesize, Facesize));
  436. f->mask = nil;
  437. }
  438. }