Commit Diff


commit - 4a18fa68b01bf8121a8660d3f5214e5927763251
commit + 9b4a2324d39a8c952c8184249d2b06b9349205a2
blob - 77efeb4eab218790d31a02d94c9a4e305f1bc3aa
blob + a72d460c41b079bc83078cb5c04dc8a87b4af7fa
--- include/9p.h
+++ include/9p.h
@@ -206,6 +206,7 @@ struct Srv {
 	int		leavefdsopen;	/* magic for acme win */
 	int		dotu;
 	int		foreground;	/* run in foreground */
+	int		fake;
 
 /* below is implementation-specific; don't use */
 	Fidpool*	fpool;
blob - 484f2d3cd2a2c9df805302d3f7090ab1ddece715
blob + a2f21384feac7df84146ce39dce1d74fa430b57e
--- man/man1/9term.1
+++ man/man1/9term.1
@@ -158,7 +158,7 @@ for the preceding string (see
 .PP
 Text may be moved vertically within the window.
 A scroll bar on the left of the window shows in its clear portion what fragment of the
-total output text is visible on the screen, and in its gray part what
+total output text is visible on the screen, and in its grey part what
 is above or below view;
 it measures characters, not lines.
 Mousing inside the scroll bar moves text:
blob - /dev/null
blob + 3bad44aea09ce6770557969768b1e2c57cbfa3a3 (mode 644)
--- /dev/null
+++ man/man4/fontsrv.4
@@ -0,0 +1,126 @@
+.TH FONTSRV 4
+.SH NAME
+fontsrv \- file system access to host fonts
+.SH SYNOPSIS
+.B fontsrv
+[
+.B -m
+.I mtpt
+]
+.PP
+.B fontsrv
+.B -p
+.I path
+.SH DESCRIPTION
+.I Fontsrv
+presents the host window system's fonts
+in the standard Plan 9 format
+(see
+.IR font (7)).
+It serves a virtual directory tree mounted at
+.I mtpt
+(if the
+.B -m
+option is given)
+and posted at
+.I srvname 
+(default
+.IR font ).
+.PP
+The 
+.B -p
+option changes 
+.I fontsrv 's
+behavior: rather than serve a file system,
+.I fontsrv
+prints to standard output the contents of the named 
+.IR path .
+If
+.I path
+names a directory in the served file system,
+.I fontsrv
+lists the directory's contents.
+.PP
+The fonts are arranged in a two-level tree.
+The root contains directories named for each system font.
+Each font directory contains subdirectories named for
+a point size and whether the subfonts are anti-aliased:
+.B 10
+(bitmap)
+.BR 10a
+(anti-aliased greyscale)
+.BR 12 ,
+.BR 12a ,
+and so on.
+The font directory will synthesize additional sizes on
+demand: looking up
+.B 19a
+will synthesize the 19-point anti-aliased size
+if possible.
+Each size directory contains a
+.B font
+file and subfont files
+named
+.BR x0000.bit ,
+.BR x0100.bit ,
+and so on
+representing 256-character Unicode ranges.
+.PP
+.I Openfont
+(see
+.IR graphics (3))
+recognizes font paths beginning with 
+.B /mnt/font
+and implements them by invoking
+.IR fontsrv ;
+it need not be running already.
+.SH EXAMPLES
+List the fonts on the system:
+.IP
+.EX
+% fontsrv &
+% 9p ls font
+.EE
+.LP
+or:
+.IP
+.EX
+% fontsrv -p .
+.EE
+.LP
+Run 
+.IR acme (1)
+using the operating system's Monaco as the fixed-width font:
+.IP
+.EX
+% acme -F /mnt/font/Monaco/13a/font
+.EE
+.LP
+Run
+.IR sam (1)
+using the same font:
+.IP
+.EX
+font=/mnt/font/Monaco/13a/font sam
+.EE
+.SH SOURCE
+.B \*9/src/cmd/fontsrv
+.SH SEE ALSO
+.IR font (7)
+.SH BUGS
+.PP
+Due to OS X restrictions,
+.I fontsrv
+does not fork itself into the background
+when serving a user-level file system.
+.PP
+.I Fontsrv
+has no support for X11 fonts;
+on X11 systems, it will serve an empty top-level directory.
+.PP
+On OS X, the anti-aliased bitmaps are not perfect.
+For example, the lower case r in the subfont
+.B Times-Roman/14a/x0000.bit
+appears truncated on the right and
+too light overall.
+
blob - /dev/null
blob + 0781fbab5abee3b54560adbb86bc3d1d11374010 (mode 644)
--- /dev/null
+++ src/cmd/fontsrv/a.h
@@ -0,0 +1,23 @@
+typedef struct XFont XFont;
+XFont *xfont;
+int nxfont;
+
+struct XFont
+{
+	char *name;
+	int loaded;
+	uchar range[256];	// range[i] == whether to have subfont i<<8 to (i+1)<<8.
+	int nrange;
+	int unit;
+	double height;
+	double originy;
+};
+
+void	loadfonts(void);
+void	load(XFont*);
+Memsubfont*	mksubfont(char*, int, int, int, int);
+
+extern XFont *xfont;
+extern int nxfont;
+void *emalloc9p(ulong);
+extern Memsubfont *defont;
blob - /dev/null
blob + c8a179df17ff6aa99418889797a63e4f78bbcc62 (mode 644)
--- /dev/null
+++ src/cmd/fontsrv/main.c
@@ -0,0 +1,594 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+/*
+ * we included thread.h in order to include 9p.h,
+ * but we don't use threads, so exits is ok.
+ */
+#undef exits
+
+#include "a.h"
+
+Memsubfont *defont;
+
+void
+usage(void)
+{
+	fprint(2, "usage: fontsrv [-m mtpt]\n");
+	fprint(2, "or fontsrv -p path\n");
+	exits("usage");
+}
+
+static
+void
+packinfo(Fontchar *fc, uchar *p, int n)
+{
+	int j;
+
+	for(j=0;  j<=n;  j++){
+		p[0] = fc->x;
+		p[1] = fc->x>>8;
+		p[2] = fc->top;
+		p[3] = fc->bottom;
+		p[4] = fc->left;
+		p[5] = fc->width;
+		fc++;
+		p += 6;
+	}
+}
+
+enum
+{
+	Qroot = 0,
+	Qfontdir,
+	Qsizedir,
+	Qfontfile,
+	Qsubfontfile,
+};
+
+#define QTYPE(p) ((p) & 0xF)
+#define QFONT(p) (((p) >> 4) & 0xFFFF)
+#define QSIZE(p) (((p) >> 20) & 0xFF)
+#define QANTIALIAS(p) (((p) >> 28) & 0x1)
+#define QRANGE(p) (((p) >> 29) & 0xFF)
+static int sizes[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 28 };
+
+static vlong
+qpath(int type, int font, int size, int antialias, int range)
+{
+	return type | (font << 4) | (size << 20) | (antialias << 28) | ((vlong)range << 29);
+}
+
+static void
+dostat(vlong path, Qid *qid, Dir *dir)
+{
+	char *name;
+	Qid q;
+	ulong mode;
+	vlong length;
+	XFont *f;
+	char buf[100];
+	
+	q.type = 0;
+	q.vers = 0;
+	q.path = path;
+	mode = 0444;
+	length = 0;
+	name = "???";
+
+	switch(QTYPE(path)) {
+	default:
+		sysfatal("dostat %#llux", path);
+
+	case Qroot:
+		q.type = QTDIR;
+		name = "/";
+		break;
+
+	case Qfontdir:
+		q.type = QTDIR;
+		f = &xfont[QFONT(path)];
+		name = f->name;
+		break;
+
+	case Qsizedir:
+		q.type = QTDIR;
+		snprint(buf, sizeof buf, "%lld%s", QSIZE(path), QANTIALIAS(path) ? "a" : "");
+		name = buf;
+		break;
+	
+	case Qfontfile:
+		f = &xfont[QFONT(path)];
+		load(f);
+		length = 11+1+11+1+f->nrange*(6+1+6+1+9+1);
+		name = "font";
+		break;
+
+	case Qsubfontfile:
+		snprint(buf, sizeof buf, "x%02llx00.bit", QRANGE(path));
+		name = buf;
+		break;
+	}
+	
+	if(qid)
+		*qid = q;
+	if(dir) {
+		memset(dir, 0, sizeof *dir);
+		dir->name = estrdup9p(name);
+		dir->muid = estrdup9p("");
+		dir->uid = estrdup9p("font");
+		dir->gid = estrdup9p("font");
+		dir->qid = q;
+		if(q.type == QTDIR)
+			mode |= DMDIR | 0111;
+		dir->mode = mode;
+		dir->length = length;
+	}
+}
+
+static char*
+xwalk1(Fid *fid, char *name, Qid *qid)
+{
+	int i, dotdot;
+	vlong path;
+	char *p;
+	int a, n;
+	XFont *f;
+
+	path = fid->qid.path;
+	dotdot = strcmp(name, "..") == 0;
+	switch(QTYPE(path)) {
+	default:
+	NotFound:
+		return "file not  found";
+
+	case Qroot:
+		if(dotdot)
+			break;
+		for(i=0; i<nxfont; i++) {
+			if(strcmp(xfont[i].name, name) == 0) {
+				path = qpath(Qfontdir, i, 0, 0, 0);
+				goto Found;
+			}
+		}
+		goto NotFound;
+
+	case Qfontdir:
+		if(dotdot) {
+			path = Qroot;
+			break;
+		}
+		n = strtol(name, &p, 10);
+		if(n == 0)
+			goto NotFound;
+		a = 0;
+		if(*p == 'a') {
+			a = 1;
+			p++;
+		}
+		if(*p != 0)
+			goto NotFound;
+		path += Qsizedir - Qfontdir + qpath(0, 0, n, a, 0);
+		break;
+
+	case Qsizedir:
+		if(dotdot) {
+			path = qpath(Qfontdir, QFONT(path), 0, 0, 0);
+			break;
+		}
+		if(strcmp(name, "font") == 0) {
+			path += Qfontfile - Qsizedir;
+			break;
+		}
+		f = &xfont[QFONT(path)];
+		load(f);
+		p = name;
+		if(*p != 'x')
+			goto NotFound;
+		p++;
+		n = strtoul(p, &p, 16);
+		if(p != name+5 || (n&0xFF) != 0 || strcmp(p, ".bit") != 0 || !f->range[(n>>8) & 0xFF])
+			goto NotFound;
+		path += Qsubfontfile - Qsizedir + qpath(0, 0, 0, 0, (n>>8) & 0xFF);
+		break;
+	}
+Found:
+	dostat(path, qid, nil);
+	fid->qid = *qid;
+	return nil;
+}
+
+static int
+rootgen(int i, Dir *d, void *v)
+{
+	if(i >= nxfont)
+		return -1;
+	dostat(qpath(Qfontdir, i, 0, 0, 0), nil, d);
+	return 0;
+}
+
+static int
+fontgen(int i, Dir *d, void *v)
+{
+	vlong path;
+	Fid *f;
+	
+	f = v;
+	path = f->qid.path;
+	if(i >= 2*nelem(sizes))
+		return -1;
+	dostat(qpath(Qsizedir, QFONT(path), sizes[i/2], i&1, 0), nil, d);
+	return 0;
+}
+
+static int
+sizegen(int i, Dir *d, void *v)
+{
+	vlong path;
+	Fid *fid;
+	XFont *f;
+	int j;
+
+	fid = v;
+	path = fid->qid.path;
+	if(i == 0) {
+		path += Qfontfile - Qsizedir;
+		goto Done;
+	}
+	i--;
+	f = &xfont[QFONT(path)];
+	load(f);
+	for(j=0; j<nelem(f->range); j++) {
+		if(f->range[j] == 0)
+			continue;
+		if(i == 0) {
+			path += Qsubfontfile - Qsizedir;
+			path += qpath(0, 0, 0, 0, j);
+			goto Done;
+		}
+		i--;
+	}
+	return -1;
+
+Done:
+	dostat(path, nil, d);
+	return 0;
+}
+
+static void
+xattach(Req *r)
+{
+	dostat(0, &r->ofcall.qid, nil);
+	r->fid->qid = r->ofcall.qid;
+	respond(r, nil);
+}
+
+static void
+xopen(Req *r)
+{
+	if(r->ifcall.mode != OREAD) {
+		respond(r, "permission denied");
+		return;
+	}
+	r->ofcall.qid = r->fid->qid;
+	respond(r, nil);
+}
+
+void
+responderrstr(Req *r)
+{
+	char err[ERRMAX];
+	
+	rerrstr(err, sizeof err);
+	respond(r, err);
+}
+
+static void
+xread(Req *r)
+{
+	int i, size, height, ascent;
+	vlong path;
+	Fmt fmt;
+	XFont *f;
+	char *data;
+	Memsubfont *sf;
+	Memimage *m;
+	
+	path = r->fid->qid.path;
+	switch(QTYPE(path)) {
+	case Qroot:
+		dirread9p(r, rootgen, nil);
+		break;
+	case Qfontdir:
+		dirread9p(r, fontgen, r->fid);
+		break;
+	case Qsizedir:
+		dirread9p(r, sizegen, r->fid);
+		break;
+	case Qfontfile:
+		fmtstrinit(&fmt);
+		f = &xfont[QFONT(path)];
+		load(f);
+		if(f->unit == 0)
+			break;
+		height = f->height * (int)QSIZE(path)/f->unit + 0.99999999;
+		ascent = height - (int)(-f->originy * (int)QSIZE(path)/f->unit + 0.99999999);
+		fmtprint(&fmt, "%11d %11d\n", height, ascent);
+		for(i=0; i<nelem(f->range); i++) {
+			if(f->range[i] == 0)
+				continue;
+			fmtprint(&fmt, "0x%04x 0x%04x x%04x.bit\n", i<<8, (i<<8) + 0xFF, i<<8);
+		}
+		data = fmtstrflush(&fmt);
+		readstr(r, data);
+		free(data);
+		break;
+	case Qsubfontfile:
+		f = &xfont[QFONT(path)];
+		load(f);
+		if(r->fid->aux == nil) {
+			r->fid->aux = mksubfont(f->name, QRANGE(path)<<8, (QRANGE(path)<<8)+0xFF, QSIZE(path), QANTIALIAS(path));
+			if(r->fid->aux == nil) {
+				responderrstr(r);
+				return;
+			}
+		}
+		sf = r->fid->aux;
+		m = sf->bits;
+		if(r->ifcall.offset < 5*12) {
+			char *chan;
+			if(QANTIALIAS(path))
+				chan = "k8";
+			else
+				chan = "k1";
+			data = smprint("%11s %11d %11d %11d %11d ", chan, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y);
+			readstr(r, data);
+			free(data);
+			break;
+		}
+		r->ifcall.offset -= 5*12;
+		size = bytesperline(m->r, chantodepth(m->chan)) * Dy(m->r);
+		if(r->ifcall.offset < size) {
+			readbuf(r, byteaddr(m, m->r.min), size);
+			break;
+		}
+		r->ifcall.offset -= size;
+		data = emalloc9p(3*12+6*(sf->n+1));
+		sprint(data, "%11d %11d %11d ", sf->n, sf->height, sf->ascent);
+		packinfo(sf->info, (uchar*)data+3*12, sf->n);
+		readbuf(r, data, 3*12+6*(sf->n+1));
+		free(data);
+		break;
+	}
+	respond(r, nil);
+}
+
+static void
+xdestroyfid(Fid *fid)
+{
+	Memsubfont *sf;
+	
+	sf = fid->aux;
+	if(sf == nil)
+		return;
+
+	freememimage(sf->bits);
+	free(sf->info);
+	free(sf);
+	fid->aux = nil;
+}
+
+static void
+xstat(Req *r)
+{
+	dostat(r->fid->qid.path, nil, &r->d);
+	respond(r, nil);
+}
+
+Srv xsrv;
+
+int
+proccreate(void (*f)(void*), void *a, unsigned i)
+{
+	abort();
+}
+
+int pflag;
+
+static long dirpackage(uchar*, long, Dir**);
+
+void
+dump(char *path)
+{
+	char *elem, *p, *path0, *err;
+	uchar buf[4096];
+	Fid fid;
+	Qid qid;
+	Dir *d;
+	Req r;
+	int off, i, n;
+
+	// root
+	memset(&fid, 0, sizeof fid);
+	dostat(0, &fid.qid, nil);	
+	qid = fid.qid;
+
+	path0 = path;
+	while(path != nil) {
+		p = strchr(path, '/');
+		if(p != nil)
+			*p = '\0';
+		elem = path;
+		if(strcmp(elem, "") != 0 && strcmp(elem, ".") != 0) {
+			err = xwalk1(&fid, elem, &qid);
+			if(err != nil) {
+				fprint(2, "%s: %s\n", path0, err);
+				exits(err);
+			}
+		}
+		if(p)
+			*p++ = '/';
+		path = p;
+	}
+	
+	memset(&r, 0, sizeof r);
+	xsrv.fake = 1;
+
+	// read and display
+	off = 0;
+	for(;;) {
+		r.srv = &xsrv;
+		r.fid = &fid;
+		r.ifcall.type = Tread;
+		r.ifcall.count = sizeof buf;
+		r.ifcall.offset = off;
+		r.ofcall.data = (char*)buf;
+		r.ofcall.count = 0;
+		xread(&r);
+		if(r.ofcall.type != Rread) {
+			fprint(2, "reading %s: %s\n", path0, r.ofcall.ename);
+			exits(r.ofcall.ename);
+		}
+		n = r.ofcall.count;
+		if(n == 0)
+			break;
+		if(off == 0 && pflag > 1) {
+			print("\001");
+		}
+		off += n;
+		if(qid.type & QTDIR) {
+			n = dirpackage(buf, n, &d);
+			for(i=0; i<n; i++)
+				print("%s%s\n", d[i].name, (d[i].mode&DMDIR) ? "/" : "");
+			free(d);
+		} else
+			write(1, buf, n);
+	}
+}
+
+int
+fontcmp(const void *va, const void *vb)
+{
+	XFont *a, *b;
+
+	a = (XFont*)va;
+	b = (XFont*)vb;
+	return strcmp(a->name, b->name);
+}
+
+void
+main(int argc, char **argv)
+{
+	char *mtpt;
+	
+	mtpt = unsharp("#9/font/mnt");
+
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 'F':
+		chattyfuse++;
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 'p':
+		pflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+	
+	xsrv.attach = xattach;
+	xsrv.open = xopen;
+	xsrv.read = xread;
+	xsrv.stat = xstat;
+	xsrv.walk1 = xwalk1;
+	xsrv.destroyfid = xdestroyfid;
+
+	fmtinstall('R', Rfmt);
+	fmtinstall('P', Pfmt);
+	memimageinit();
+	defont = getmemdefont();
+	loadfonts();
+	qsort(xfont, nxfont, sizeof xfont[0], fontcmp);
+	
+	if(pflag) {
+		if(argc != 1 || chatty9p || chattyfuse)
+			usage();
+		dump(argv[0]);
+		exits(0);
+	}
+
+	if(pflag || argc != 0)
+		usage();
+
+	/*
+	 * Check twice -- if there is an exited instance
+	 * mounted there, the first access will fail but unmount it.
+	 */
+	if(mtpt && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
+		sysfatal("mountpoint %s does not exist", mtpt);
+
+	xsrv.foreground = 1;
+	threadpostmountsrv(&xsrv, "font", mtpt, 0);
+}
+
+/*
+	/sys/src/libc/9sys/dirread.c
+*/
+static
+long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+	char *s;
+	long ss, i, n, nn, m;
+
+	*d = nil;
+	if(ts <= 0)
+		return 0;
+
+	/*
+	 * first find number of all stats, check they look like stats, & size all associated strings
+	 */
+	ss = 0;
+	n = 0;
+	for(i = 0; i < ts; i += m){
+		m = BIT16SZ + GBIT16(&buf[i]);
+		if(statcheck(&buf[i], m) < 0)
+			break;
+		ss += m;
+		n++;
+	}
+
+	if(i != ts)
+		return -1;
+
+	*d = malloc(n * sizeof(Dir) + ss);
+	if(*d == nil)
+		return -1;
+
+	/*
+	 * then convert all buffers
+	 */
+	s = (char*)*d + n * sizeof(Dir);
+	nn = 0;
+	for(i = 0; i < ts; i += m){
+		m = BIT16SZ + GBIT16((uchar*)&buf[i]);
+		if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
+			free(*d);
+			*d = nil;
+			return -1;
+		}
+		nn++;
+		s += m;
+	}
+
+	return nn;
+}
+
blob - /dev/null
blob + 27b6b3ce237e2e6087d56d3864d3a87ed40ebfd7 (mode 644)
--- /dev/null
+++ src/cmd/fontsrv/mkfile
@@ -0,0 +1,14 @@
+<|sh ../devdraw/mkwsysrules.sh
+<$PLAN9/src/mkhdr
+
+TARG=fontsrv
+
+HFILES=a.h
+
+OFILES=\
+	main.$O\
+	$WSYSTYPE.$O\
+
+<$PLAN9/src/mkone
+
+
blob - /dev/null
blob + eb3b57a9a01ed03944eb74853c835e1ee6251247 (mode 644)
--- /dev/null
+++ src/cmd/fontsrv/nowsys.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "a.h"
+
+void
+loadfonts(void)
+{
+}
+
+void
+load(XFont *f)
+{
+}
+
+Memsubfont*
+mksubfont(char *name, int lo, int hi, int size, int antialias)
+{
+	return nil;
+}
blob - /dev/null
blob + 94190d191126c80e45353561bf40e98fd9bc648b (mode 644)
--- /dev/null
+++ src/cmd/fontsrv/osx.c
@@ -0,0 +1,292 @@
+#include <u.h>
+
+#define Point OSXPoint
+#define Rect OSXRect
+#define Cursor OSXCursor
+#include <Carbon/Carbon.h>
+#undef Rect
+#undef Point
+#undef Cursor
+#undef offsetof
+#undef nil
+
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "a.h"
+
+
+extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);
+
+char*
+mac2c(CFStringRef s)
+{
+	char *p;
+	int n;
+
+	n = CFStringGetLength(s)*8;	
+	p = malloc(n);
+	CFStringGetCString(s, p, n, kCFStringEncodingUTF8);
+	return p;
+}
+
+CFStringRef
+c2mac(char *p)
+{
+	return CFStringCreateWithBytes(nil, (uchar*)p, strlen(p), kCFStringEncodingUTF8, false);
+}
+
+Rectangle
+mac2r(CGRect r, int size, int unit)
+{
+	Rectangle rr;
+
+	rr.min.x = r.origin.x*size/unit;
+	rr.min.y = r.origin.y*size/unit;
+	rr.max.x = (r.origin.x+r.size.width)*size/unit + 0.99999999;
+	rr.max.y = (r.origin.x+r.size.width)*size/unit + 0.99999999;
+	return rr;
+}
+
+void
+loadfonts(void)
+{
+	int i, n;
+	CTFontCollectionRef allc;
+	CFArrayRef array;
+	CFStringRef s;
+	CTFontDescriptorRef f;
+
+	allc = CTFontCollectionCreateFromAvailableFonts(0);
+	array = CTFontCollectionCreateMatchingFontDescriptors(allc);
+	n = CFArrayGetCount(array);
+	xfont = emalloc9p(n*sizeof xfont[0]);
+	for(i=0; i<n; i++) {
+		f = (void*)CFArrayGetValueAtIndex(array, i);
+		if(f == nil)
+			continue;
+		s = CTFontDescriptorCopyAttribute(f, kCTFontNameAttribute);
+		xfont[nxfont].name = mac2c(s);		
+		CFRelease(s);
+		nxfont++;
+	}
+}
+
+CGRect
+subfontbbox(CGFontRef font, int lo, int hi)
+{
+	int i, first;
+	CGRect bbox;
+
+	bbox.origin.x = 0;
+	bbox.origin.y = 0;
+	bbox.size.height = 0;
+	bbox.size.width = 0;
+
+	first = 1;
+	for(i=lo; i<=hi; i++) {
+		UniChar u;
+		CGGlyph g;
+		CGRect r;
+
+		u = i;
+		CGFontGetGlyphsForUnichars(font, &u, &g, 1);
+		if(g == 0 || !CGFontGetGlyphBBoxes(font, &g, 1, &r))
+			continue;
+
+		r.size.width += r.origin.x;
+		r.size.height += r.origin.y;
+		if(first) {
+			bbox = r;
+			first = 0;
+			continue;
+		}
+		if(bbox.origin.x > r.origin.x)
+			bbox.origin.x = r.origin.x;	
+		if(bbox.origin.y > r.origin.y)
+			bbox.origin.y = r.origin.y;	
+		if(bbox.size.width < r.size.width)
+			bbox.size.width = r.size.width;
+		if(bbox.size.height < r.size.height)
+			bbox.size.height = r.size.height;
+	}
+	
+	bbox.size.width -= bbox.origin.x;
+	bbox.size.height -= bbox.origin.y;
+	return bbox;
+}
+
+void
+load(XFont *f)
+{
+	int i, j;
+	CGFontRef font;
+	CFStringRef s;
+	UniChar u[256];
+	CGGlyph g[256];
+	CGRect bbox;
+
+	if(f->loaded)
+		return;
+	f->loaded = 1;
+	s = c2mac(f->name);
+	font = CGFontCreateWithFontName(s);
+	CFRelease(s);
+	if(font == nil)
+		return;
+	
+	// assume bbox gives latin1 is height/ascent for all
+	bbox = subfontbbox(font, 0x00, 0xff);
+	f->unit = CGFontGetUnitsPerEm(font);
+	f->height = bbox.size.height;
+	f->originy = bbox.origin.y;
+
+	// figure out where the letters are
+	for(i=0; i<0xffff; i+=0x100) {
+		for(j=0; j<0x100; j++) {
+			u[j] = i+j;
+			g[j] = 0;
+		}
+		CGFontGetGlyphsForUnichars(font, u, g, 256);
+		for(j=0; j<0x100; j++) {
+			if(g[j] != 0) {
+				f->range[i>>8] = 1;
+				f->nrange++;
+				break;
+			}
+		}
+	}
+	CFRelease(font);
+}
+
+Memsubfont*
+mksubfont(char *name, int lo, int hi, int size, int antialias)
+{
+	CFStringRef s;
+	CGColorSpaceRef color;
+	CGContextRef ctxt;
+	CGFontRef font;
+	CGRect bbox;
+	Memimage *m, *mc, *m1;
+	int x, y, y0;
+	int i, unit;
+	Fontchar *fc, *fc0;
+	Memsubfont *sf;
+
+	s = c2mac(name);
+	font = CGFontCreateWithFontName(s);
+	CFRelease(s);
+	if(font == nil)
+		return nil;
+	bbox = subfontbbox(font, lo, hi);
+	unit = CGFontGetUnitsPerEm(font);
+	x = (int)(bbox.size.width * size / unit + 0.99999999);
+	y = bbox.size.height * size/unit + 0.99999999;
+	y0 = (int)(-bbox.origin.y * size/unit + 0.99999999);
+	m = allocmemimage(Rect(0, 0, x*(hi+1-lo), y), GREY8);
+	if(m == nil)
+		return nil;
+	mc = allocmemimage(Rect(0, 0, x, y), GREY8);
+	if(mc == nil)
+		return nil;
+	memfillcolor(m, DBlack);
+	memfillcolor(mc, DBlack);
+	fc = malloc((hi+2 - lo) * sizeof fc[0]);
+	sf = malloc(sizeof *sf);
+	if(fc == nil || sf == nil) {
+		freememimage(m);
+		freememimage(mc);
+		free(fc);
+		free(sf);
+		return nil;
+	}
+	fc0 = fc;
+
+	color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+	ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8,
+		mc->width*sizeof(u32int), color, kCGImageAlphaNone);
+	CGColorSpaceRelease(color);
+	if(ctxt == nil) {
+		freememimage(m);
+		freememimage(mc);
+		free(fc);
+		free(sf);
+		return nil;
+	}
+
+	CGContextSetFont(ctxt, font);
+	CGContextSetFontSize(ctxt, size);
+	CGContextSetAllowsAntialiasing(ctxt, antialias);
+	CGContextSetRGBFillColor(ctxt, 1, 1, 1, 1);
+	CGContextSetTextPosition(ctxt, 0, 0);	// XXX
+
+	x = 0;
+	for(i=lo; i<=hi; i++, fc++) {
+		UniChar u[2];
+		CGGlyph g[2];
+		CGRect r[2];
+		CGPoint p1;
+		int n;
+
+		fc->x = x;
+		fc->top = 0;
+		fc->bottom = Dy(m->r);
+
+		n = 0;
+		u[n++] = i;
+		if(0)	// debugging
+			u[n++] = '|';
+		g[0] = 0;
+		CGFontGetGlyphsForUnichars(font, u, g, n);
+		if(g[0] == 0 || !CGFontGetGlyphBBoxes(font, g, n, r)) {
+		None:
+			fc->width = 0;
+			if(i == 0) {
+				Point p;
+				Fontchar *i;
+				p = Pt(x, y0);
+				// memimagestring(m, p, memwhite, ZP, defont, peterface);
+				i = defont->info + 0;
+				memdraw(m, Rect(p.x+i->left, p.y+i->top, p.x+i->left+(i[1].x-i[0].x), p.y+i->bottom),
+					memwhite, ZP, defont->bits, Pt(i->x, i->top), S);
+				p.x += i->width;
+				fc->left = i->left;
+				fc->width = i->width;
+				x = p.x;
+			}	
+			continue;
+		}
+		memfillcolor(mc, DBlack);
+		CGContextSetTextPosition(ctxt, 0, y0);
+		CGContextShowGlyphs(ctxt, g, n);
+		p1 = CGContextGetTextPosition(ctxt);
+		if(p1.x <= 0)
+			goto None;
+		memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S);
+		fc->width = p1.x;
+		fc->left = 0;
+		x += p1.x;
+	}
+	fc->x = x;
+
+	// round up to 32-bit boundary
+	// so that in-memory data is same
+	// layout as in-file data.
+	if(antialias)
+		x += -x & 3;
+	else
+		x += -x & 31;
+	m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1);
+	memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S);
+	freememimage(m);
+
+	sf->name = nil;
+	sf->n = hi+1 - lo;
+	sf->height = Dy(m1->r);
+	sf->ascent = Dy(m1->r) - y0;
+	sf->info = fc0;
+	sf->bits = m1;
+	
+	return sf;
+}
+
blob - /dev/null
blob + f1fb3cd930612e6761f49b72f6b2ec754670c2ff (mode 644)
--- /dev/null
+++ src/cmd/fontsrv/x11.c
@@ -0,0 +1,2 @@
+/* maybe someday */
+#include "nowsys.c"
blob - 0e7c311345bee0eff8587b704af9641c28d26e9f
blob + ece35940352f850c6077846acd73f8e9dcc37d8e
--- src/lib9p/srv.c
+++ src/lib9p/srv.c
@@ -794,6 +794,9 @@ respond(Req *r, char *error)
 	if(r->error)
 		setfcallerror(&r->ofcall, r->error);
 
+	if(srv->fake)
+		return;
+
 if(chatty9p)
 	fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
 
blob - 2cbec8126474c33a6b17d546d8caf5ef2c3d1a0d
blob + 0d8be9ff4e2a99a9a0e74d449e5bc2010cd88237
--- src/libdraw/getsubfont.c
+++ src/libdraw/getsubfont.c
@@ -6,6 +6,8 @@
  * Default version: treat as file name
  */
 
+int _fontpipe(char*);
+
 Subfont*
 _getsubfont(Display *d, char *name)
 {
@@ -13,7 +15,9 @@ _getsubfont(Display *d, char *name)
 	Subfont *f;
 
 	fd = open(name, OREAD);
-		
+	if(fd < 0 && strncmp(name, "/mnt/font/", 10) == 0)
+		fd = _fontpipe(name+10);
+
 	if(fd < 0){
 		fprint(2, "getsubfont: can't open %s: %r\n", name);
 		return 0;
blob - 05370eacc95899850aaadde56bfd897dd27c6fbe
blob + 892f7f6137d2114412e079714f3c2a9e963114e2
--- src/libdraw/openfont.c
+++ src/libdraw/openfont.c
@@ -3,6 +3,7 @@
 #include <draw.h>
 
 extern vlong _drawflength(int);
+int _fontpipe(char*);
 
 Font*
 openfont(Display *d, char *name)
@@ -27,26 +28,78 @@ openfont(Display *d, char *name)
 		}
 		name = nambuf;
 	}
+	if(fd >= 0)
+		n = _drawflength(fd);
+	if(fd < 0 && strncmp(name, "/mnt/font/", 10) == 0) {
+		fd = _fontpipe(name+10);
+		n = 8192;
+	}
 	if(fd < 0)
 		return 0;
 
-	n = _drawflength(fd);
 	buf = malloc(n+1);
 	if(buf == 0){
 		close(fd);
 		free(nambuf);
 		return 0;
 	}
-	buf[n] = 0;
-	i = read(fd, buf, n);
+	i = readn(fd, buf, n);
 	close(fd);
-	if(i != n){
+	if(i <= 0){
 		free(buf);
 		free(nambuf);
 		return 0;
 	}
+	buf[i] = 0;
 	fnt = buildfont(d, buf, name);
 	free(buf);
 	free(nambuf);
 	return fnt;
 }
+
+int
+_fontpipe(char *name)
+{
+	int p[2];
+	char c;
+	char buf[1024], *argv[10];
+	int nbuf, pid;
+	
+	if(pipe(p) < 0)
+		return -1;
+	pid = rfork(RFNOWAIT|RFFDG|RFPROC);
+	if(pid < 0) {
+		close(p[0]);
+		close(p[1]);
+		return -1;
+	}
+	if(pid == 0) {
+		close(p[0]);
+		dup(p[1], 1);
+		dup(p[1], 2);
+		if(p[1] > 2)
+			close(p[1]);
+		argv[0] = "fontsrv";
+		argv[1] = "-pp";
+		argv[2] = name;
+		argv[3] = nil;
+		execvp("fontsrv", argv);
+		print("exec fontsrv: %r\n");
+		_exit(0);
+	}
+	close(p[1]);
+	
+	// success marked with leading \001.
+	// otherwise an error happened.
+	for(nbuf=0; nbuf<sizeof buf-1; nbuf++) {
+		if(read(p[0], &c, 1) < 1 || c == '\n') {
+			buf[nbuf] = '\0';
+			werrstr(buf);
+			close(p[0]);
+			return -1;
+		}
+		if(c == '\001')
+			break;
+	}
+	return p[0];
+}
blob - 1374ef31b0d8c8ddaca9f43db992966545253e84
blob + 874528befd5520b41b107d347d5616de63b3cad4
--- src/libdraw/subfontname.c
+++ src/libdraw/subfontname.c
@@ -43,7 +43,7 @@ subfontname(char *cfname, char *fname, int maxdepth)
 	}
 
 	/* try default */
-	if(access(t, AREAD) == 0)
+	if(strncmp(t, "/mnt/font/", 10) == 0 || access(t, AREAD) == 0)
 		return t;
 
 	return nil;