1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <thread.h>
6 #include <fcall.h>
7 #include <9p.h>
8 /*
9  * we included thread.h in order to include 9p.h,
10  * but we don't use threads, so exits is ok.
11  */
12 #undef exits
13 
14 #include "a.h"
15 
16 void
usage(void)17 usage(void)
18 {
19 	fprint(2, "usage: fontsrv [-m mtpt]\n");
20 	fprint(2, "or fontsrv -p path\n");
21 	exits("usage");
22 }
23 
24 static
25 void
packinfo(Fontchar * fc,uchar * p,int n)26 packinfo(Fontchar *fc, uchar *p, int n)
27 {
28 	int j;
29 
30 	for(j=0;  j<=n;  j++){
31 		p[0] = fc->x;
32 		p[1] = fc->x>>8;
33 		p[2] = fc->top;
34 		p[3] = fc->bottom;
35 		p[4] = fc->left;
36 		p[5] = fc->width;
37 		fc++;
38 		p += 6;
39 	}
40 }
41 
42 enum
43 {
44 	Qroot = 0,
45 	Qfontdir,
46 	Qsizedir,
47 	Qfontfile,
48 	Qsubfontfile,
49 };
50 
51 #define QTYPE(p) ((p) & 0xF)
52 #define QFONT(p) (((p) >> 4) & 0xFFFF)
53 #define QSIZE(p) (((p) >> 20) & 0xFF)
54 #define QANTIALIAS(p) (((p) >> 28) & 0x1)
55 #define QRANGE(p) (((p) >> 29) & 0xFFFFFF)
56 static int sizes[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 28 };
57 
58 static vlong
qpath(int type,int font,int size,int antialias,int range)59 qpath(int type, int font, int size, int antialias, int range)
60 {
61 	return type | (font << 4) | (size << 20) | (antialias << 28) | ((vlong)range << 29);
62 }
63 
64 static void
dostat(vlong path,Qid * qid,Dir * dir)65 dostat(vlong path, Qid *qid, Dir *dir)
66 {
67 	char *name;
68 	Qid q;
69 	ulong mode;
70 	vlong length;
71 	XFont *f;
72 	char buf[100];
73 
74 	q.type = 0;
75 	q.vers = 0;
76 	q.path = path;
77 	mode = 0444;
78 	length = 0;
79 	name = "???";
80 
81 	switch(QTYPE(path)) {
82 	default:
83 		sysfatal("dostat %#llux", path);
84 
85 	case Qroot:
86 		q.type = QTDIR;
87 		name = "/";
88 		break;
89 
90 	case Qfontdir:
91 		q.type = QTDIR;
92 		f = &xfont[QFONT(path)];
93 		name = f->name;
94 		break;
95 
96 	case Qsizedir:
97 		q.type = QTDIR;
98 		snprint(buf, sizeof buf, "%lld%s", QSIZE(path), QANTIALIAS(path) ? "a" : "");
99 		name = buf;
100 		break;
101 
102 	case Qfontfile:
103 		f = &xfont[QFONT(path)];
104 		load(f);
105 		length = 11+1+11+1+f->nfile*(6+1+6+1+9+1);
106 		name = "font";
107 		break;
108 
109 	case Qsubfontfile:
110 		snprint(buf, sizeof buf, "x%04x.bit", (int)QRANGE(path)*SubfontSize);
111 		name = buf;
112 		break;
113 	}
114 
115 	if(qid)
116 		*qid = q;
117 	if(dir) {
118 		memset(dir, 0, sizeof *dir);
119 		dir->name = estrdup9p(name);
120 		dir->muid = estrdup9p("");
121 		dir->uid = estrdup9p("font");
122 		dir->gid = estrdup9p("font");
123 		dir->qid = q;
124 		if(q.type == QTDIR)
125 			mode |= DMDIR | 0111;
126 		dir->mode = mode;
127 		dir->length = length;
128 	}
129 }
130 
131 static char*
xwalk1(Fid * fid,char * name,Qid * qid)132 xwalk1(Fid *fid, char *name, Qid *qid)
133 {
134 	int i, dotdot;
135 	vlong path;
136 	char *p;
137 	int a, n;
138 	XFont *f;
139 
140 	path = fid->qid.path;
141 	dotdot = strcmp(name, "..") == 0;
142 	switch(QTYPE(path)) {
143 	default:
144 	NotFound:
145 		return "file not found";
146 
147 	case Qroot:
148 		if(dotdot)
149 			break;
150 		for(i=0; i<nxfont; i++) {
151 			if(strcmp(xfont[i].name, name) == 0) {
152 				path = qpath(Qfontdir, i, 0, 0, 0);
153 				goto Found;
154 			}
155 		}
156 		goto NotFound;
157 
158 	case Qfontdir:
159 		if(dotdot) {
160 			path = Qroot;
161 			break;
162 		}
163 		n = strtol(name, &p, 10);
164 		if(n == 0)
165 			goto NotFound;
166 		a = 0;
167 		if(*p == 'a') {
168 			a = 1;
169 			p++;
170 		}
171 		if(*p != 0)
172 			goto NotFound;
173 		path += Qsizedir - Qfontdir + qpath(0, 0, n, a, 0);
174 		break;
175 
176 	case Qsizedir:
177 		if(dotdot) {
178 			path = qpath(Qfontdir, QFONT(path), 0, 0, 0);
179 			break;
180 		}
181 		if(strcmp(name, "font") == 0) {
182 			path += Qfontfile - Qsizedir;
183 			break;
184 		}
185 		f = &xfont[QFONT(path)];
186 		load(f);
187 		p = name;
188 		if(*p != 'x')
189 			goto NotFound;
190 		p++;
191 		n = strtoul(p, &p, 16);
192 		if(p < name+5 || p > name+5 && name[1] == '0' || n%SubfontSize != 0 || n/SubfontSize >= MaxSubfont || strcmp(p, ".bit") != 0 || !f->range[n/SubfontSize])
193 			goto NotFound;
194 		path += Qsubfontfile - Qsizedir + qpath(0, 0, 0, 0, n/SubfontSize);
195 		break;
196 	}
197 Found:
198 	dostat(path, qid, nil);
199 	fid->qid = *qid;
200 	return nil;
201 }
202 
203 static int
rootgen(int i,Dir * d,void * v)204 rootgen(int i, Dir *d, void *v)
205 {
206 	if(i >= nxfont)
207 		return -1;
208 	dostat(qpath(Qfontdir, i, 0, 0, 0), nil, d);
209 	return 0;
210 }
211 
212 static int
fontgen(int i,Dir * d,void * v)213 fontgen(int i, Dir *d, void *v)
214 {
215 	vlong path;
216 	Fid *f;
217 
218 	f = v;
219 	path = f->qid.path;
220 	if(i >= 2*nelem(sizes))
221 		return -1;
222 	dostat(qpath(Qsizedir, QFONT(path), sizes[i/2], i&1, 0), nil, d);
223 	return 0;
224 }
225 
226 static int
sizegen(int i,Dir * d,void * v)227 sizegen(int i, Dir *d, void *v)
228 {
229 	vlong path;
230 	Fid *fid;
231 	XFont *f;
232 
233 	fid = v;
234 	path = fid->qid.path;
235 	if(i == 0) {
236 		path += Qfontfile - Qsizedir;
237 		goto Done;
238 	}
239 	i--;
240 	f = &xfont[QFONT(path)];
241 	load(f);
242 	if(i < f->nfile) {
243 		path += Qsubfontfile - Qsizedir;
244 		path += qpath(0, 0, 0, 0, f->file[i]);
245 		goto Done;
246 	}
247 	return -1;
248 
249 Done:
250 	dostat(path, nil, d);
251 	return 0;
252 }
253 
254 static void
xattach(Req * r)255 xattach(Req *r)
256 {
257 	dostat(0, &r->ofcall.qid, nil);
258 	r->fid->qid = r->ofcall.qid;
259 	respond(r, nil);
260 }
261 
262 static void
xopen(Req * r)263 xopen(Req *r)
264 {
265 	if(r->ifcall.mode != OREAD) {
266 		respond(r, "permission denied");
267 		return;
268 	}
269 	r->ofcall.qid = r->fid->qid;
270 	respond(r, nil);
271 }
272 
273 void
responderrstr(Req * r)274 responderrstr(Req *r)
275 {
276 	char err[ERRMAX];
277 
278 	rerrstr(err, sizeof err);
279 	respond(r, err);
280 }
281 
282 static void
xread(Req * r)283 xread(Req *r)
284 {
285 	int i, size, height, ascent;
286 	vlong path;
287 	Fmt fmt;
288 	XFont *f;
289 	char *data;
290 	Memsubfont *sf;
291 	Memimage *m;
292 
293 	path = r->fid->qid.path;
294 	switch(QTYPE(path)) {
295 	case Qroot:
296 		dirread9p(r, rootgen, nil);
297 		break;
298 	case Qfontdir:
299 		dirread9p(r, fontgen, r->fid);
300 		break;
301 	case Qsizedir:
302 		dirread9p(r, sizegen, r->fid);
303 		break;
304 	case Qfontfile:
305 		fmtstrinit(&fmt);
306 		f = &xfont[QFONT(path)];
307 		load(f);
308 		if(f->unit == 0 && f->loadheight == nil) {
309 			readstr(r, "font missing\n");
310 			break;
311 		}
312 		if(f->fonttext == nil) {
313 			height = 0;
314 			ascent = 0;
315 			if(f->unit > 0) {
316 				height = f->height * (int)QSIZE(path)/f->unit + 0.99999999;
317 				ascent = height - (int)(-f->originy * (int)QSIZE(path)/f->unit + 0.99999999);
318 			}
319 			if(f->loadheight != nil)
320 				f->loadheight(f, QSIZE(path), &height, &ascent);
321 			fmtprint(&fmt, "%11d %11d\n", height, ascent);
322 			for(i=0; i<f->nfile; i++)
323 				fmtprint(&fmt, "0x%04x 0x%04x x%04x.bit\n", f->file[i]*SubfontSize, ((f->file[i]+1)*SubfontSize) - 1, f->file[i]*SubfontSize);
324 			f->fonttext = fmtstrflush(&fmt);
325 			f->nfonttext = strlen(f->fonttext);
326 		}
327 		readbuf(r, f->fonttext, f->nfonttext);
328 		break;
329 	case Qsubfontfile:
330 		f = &xfont[QFONT(path)];
331 		load(f);
332 		if(r->fid->aux == nil) {
333 			r->fid->aux = mksubfont(f, f->name, QRANGE(path)*SubfontSize, ((QRANGE(path)+1)*SubfontSize)-1, QSIZE(path), QANTIALIAS(path));
334 			if(r->fid->aux == nil) {
335 				responderrstr(r);
336 				return;
337 			}
338 		}
339 		sf = r->fid->aux;
340 		m = sf->bits;
341 		if(r->ifcall.offset < 5*12) {
342 			char *chan;
343 			if(QANTIALIAS(path))
344 				chan = "k8";
345 			else
346 				chan = "k1";
347 			data = smprint("%11s %11d %11d %11d %11d ", chan, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y);
348 			readstr(r, data);
349 			free(data);
350 			break;
351 		}
352 		r->ifcall.offset -= 5*12;
353 		size = bytesperline(m->r, chantodepth(m->chan)) * Dy(m->r);
354 		if(r->ifcall.offset < size) {
355 			readbuf(r, byteaddr(m, m->r.min), size);
356 			break;
357 		}
358 		r->ifcall.offset -= size;
359 		data = emalloc9p(3*12+6*(sf->n+1));
360 		sprint(data, "%11d %11d %11d ", sf->n, sf->height, sf->ascent);
361 		packinfo(sf->info, (uchar*)data+3*12, sf->n);
362 		readbuf(r, data, 3*12+6*(sf->n+1));
363 		free(data);
364 		break;
365 	}
366 	respond(r, nil);
367 }
368 
369 static void
xdestroyfid(Fid * fid)370 xdestroyfid(Fid *fid)
371 {
372 	Memsubfont *sf;
373 
374 	sf = fid->aux;
375 	if(sf == nil)
376 		return;
377 
378 	freememimage(sf->bits);
379 	free(sf->info);
380 	free(sf);
381 	fid->aux = nil;
382 }
383 
384 static void
xstat(Req * r)385 xstat(Req *r)
386 {
387 	dostat(r->fid->qid.path, nil, &r->d);
388 	respond(r, nil);
389 }
390 
391 Srv xsrv;
392 
393 int
proccreate(void (* f)(void *),void * a,unsigned i)394 proccreate(void (*f)(void*), void *a, unsigned i)
395 {
396 	abort();
397 }
398 
399 int pflag;
400 
401 static long dirpackage(uchar*, long, Dir**);
402 
403 void
dump(char * path)404 dump(char *path)
405 {
406 	char *elem, *p, *path0, *err;
407 	uchar buf[4096];
408 	Fid fid;
409 	Qid qid;
410 	Dir *d;
411 	Req r;
412 	int off, i, n;
413 
414 	// root
415 	memset(&fid, 0, sizeof fid);
416 	dostat(0, &fid.qid, nil);
417 	qid = fid.qid;
418 
419 	path0 = path;
420 	while(path != nil) {
421 		p = strchr(path, '/');
422 		if(p != nil)
423 			*p = '\0';
424 		elem = path;
425 		if(strcmp(elem, "") != 0 && strcmp(elem, ".") != 0) {
426 			err = xwalk1(&fid, elem, &qid);
427 			if(err != nil) {
428 				fprint(2, "%s: %s\n", path0, err);
429 				exits(err);
430 			}
431 		}
432 		if(p)
433 			*p++ = '/';
434 		path = p;
435 	}
436 
437 	memset(&r, 0, sizeof r);
438 	xsrv.fake = 1;
439 
440 	// read and display
441 	off = 0;
442 	for(;;) {
443 		r.srv = &xsrv;
444 		r.fid = &fid;
445 		r.ifcall.type = Tread;
446 		r.ifcall.count = sizeof buf;
447 		r.ifcall.offset = off;
448 		r.ofcall.data = (char*)buf;
449 		r.ofcall.count = 0;
450 		xread(&r);
451 		if(r.ofcall.type != Rread) {
452 			fprint(2, "reading %s: %s\n", path0, r.ofcall.ename);
453 			exits(r.ofcall.ename);
454 		}
455 		n = r.ofcall.count;
456 		if(n == 0)
457 			break;
458 		if(off == 0 && pflag > 1) {
459 			print("\001");
460 		}
461 		off += n;
462 		if(qid.type & QTDIR) {
463 			n = dirpackage(buf, n, &d);
464 			for(i=0; i<n; i++)
465 				print("%s%s\n", d[i].name, (d[i].mode&DMDIR) ? "/" : "");
466 			free(d);
467 		} else
468 			write(1, buf, n);
469 	}
470 }
471 
472 int
fontcmp(const void * va,const void * vb)473 fontcmp(const void *va, const void *vb)
474 {
475 	XFont *a, *b;
476 
477 	a = (XFont*)va;
478 	b = (XFont*)vb;
479 	return strcmp(a->name, b->name);
480 }
481 
482 void
main(int argc,char ** argv)483 main(int argc, char **argv)
484 {
485 	char *mtpt, *srvname;
486 
487 	mtpt = nil;
488 	srvname = "font";
489 
490 	ARGBEGIN{
491 	case 'D':
492 		chatty9p++;
493 		break;
494 	case 'F':
495 		chattyfuse++;
496 		break;
497 	case 'm':
498 		mtpt = EARGF(usage());
499 		break;
500 	case 's':
501 		srvname = EARGF(usage());
502 		break;
503 	case 'p':
504 		pflag++;
505 		break;
506 	default:
507 		usage();
508 	}ARGEND
509 
510 	xsrv.attach = xattach;
511 	xsrv.open = xopen;
512 	xsrv.read = xread;
513 	xsrv.stat = xstat;
514 	xsrv.walk1 = xwalk1;
515 	xsrv.destroyfid = xdestroyfid;
516 
517 	fmtinstall('R', Rfmt);
518 	fmtinstall('P', Pfmt);
519 	memimageinit();
520 	loadfonts();
521 	qsort(xfont, nxfont, sizeof xfont[0], fontcmp);
522 
523 	if(pflag) {
524 		if(argc != 1 || chatty9p || chattyfuse)
525 			usage();
526 		dump(argv[0]);
527 		exits(0);
528 	}
529 
530 	if(pflag || argc != 0)
531 		usage();
532 
533 	/*
534 	 * Check twice -- if there is an exited instance
535 	 * mounted there, the first access will fail but unmount it.
536 	 */
537 	if(mtpt && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
538 		sysfatal("mountpoint %s does not exist", mtpt);
539 
540 	xsrv.foreground = 1;
541 	threadpostmountsrv(&xsrv, srvname, mtpt, 0);
542 }
543 
544 /*
545 	/sys/src/libc/9sys/dirread.c
546 */
547 static
548 long
dirpackage(uchar * buf,long ts,Dir ** d)549 dirpackage(uchar *buf, long ts, Dir **d)
550 {
551 	char *s;
552 	long ss, i, n, nn, m;
553 
554 	*d = nil;
555 	if(ts <= 0)
556 		return 0;
557 
558 	/*
559 	 * first find number of all stats, check they look like stats, & size all associated strings
560 	 */
561 	ss = 0;
562 	n = 0;
563 	for(i = 0; i < ts; i += m){
564 		m = BIT16SZ + GBIT16(&buf[i]);
565 		if(statcheck(&buf[i], m) < 0)
566 			break;
567 		ss += m;
568 		n++;
569 	}
570 
571 	if(i != ts)
572 		return -1;
573 
574 	*d = malloc(n * sizeof(Dir) + ss);
575 	if(*d == nil)
576 		return -1;
577 
578 	/*
579 	 * then convert all buffers
580 	 */
581 	s = (char*)*d + n * sizeof(Dir);
582 	nn = 0;
583 	for(i = 0; i < ts; i += m){
584 		m = BIT16SZ + GBIT16((uchar*)&buf[i]);
585 		if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
586 			free(*d);
587 			*d = nil;
588 			return -1;
589 		}
590 		nn++;
591 		s += m;
592 	}
593 
594 	return nn;
595 }
596