shithub: KEEP

Download patch

ref: 258e99f33c065e824340532d678b9fbcce4260c3
author: glenda <glenda@pi>
date: Thu Mar 5 12:48:53 EST 2026

first upload w chat libirc pelm and netsurf

--- /dev/null
+++ b/README
@@ -1,0 +1,1 @@
+jarred, pickled, and preserved softwares that i shall not lose.
--- /dev/null
+++ b/chat/chat.man
@@ -1,0 +1,67 @@
+.TH CHAT 1
+.SH NAME
+chat \- internet relay chat client
+.SH SYNOPSIS
+.B chat
+[
+.B -t
+] [
+.B -a
+.I address
+]
+.SH DESCRIPTION
+.I Chat
+is an
+.B IRC
+client. It connects to tcp!irc.oftc.net!6697 (see
+.IR dial (2)),
+or the server specified in the
+.B $irc
+environment variable, and reads raw
+.B IRC
+commands from standard input. Chat recognizes the following flags:
+.TP
+.B -t
+Connect using TLS.
+.TP
+.B -a
+Set the address to
+.I address.
+.PP
+.I Chat
+also recognizes the following control
+messages:
+.TP
+.B /q \fItarget\fR
+This opens a new window that relays
+messages to the
+.I target.
+The
+.I target
+can be a channel or a nick.
+The created window
+also accepts control messages.
+.TP
+.BI /x
+Closes a window and parts. If sent to
+the main window, the connection is closed
+and the program exits.
+.TP
+.B /n \fIdoofus\fR
+Change nicknames to
+.I doofus.
+.SH EXAMPLES
+Join #cat-v and then open a private
+message window with glenda:
+.br
+.ne 3
+.IP
+.EX
+chat
+/q #cat-v
+/q glenda
+.EE
+.SH SOURCE
+https://github.com/mischief/plan9
+.SH SEE ALSO
+.IR ircrc (1)
--- /dev/null
+++ b/chat/dat.h
@@ -1,0 +1,15 @@
+typedef struct Wind Wind;
+struct Wind
+{
+	QLock;
+	u64int		id;
+	char		*target;
+
+	Channel		*event;		/* char* */
+
+	int			kfd;		/* rio cons */
+	int			kpid;		/* keyboard child */
+
+	Wind		*prev;
+	Wind		*next;
+};
--- /dev/null
+++ b/chat/fns.h
@@ -1,0 +1,13 @@
+/* util.c */
+u64int	jenkinshash(char*, int);
+void	riolabel(char*);
+void	rioclose(void);
+int	riowindow(char*);
+
+/* wind.c */
+Wind	*windmk(char*);
+void	windfree(Wind*);
+void	windlink(Wind*, Wind*);
+void	windunlink(Wind*, Wind*);
+Wind*	windfind(Wind*, char*);
+void	windappend(Wind*, char*, ...);
--- /dev/null
+++ b/chat/main.c
@@ -1,0 +1,315 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include <mp.h>
+#include <libsec.h>
+
+#include "irc.h"
+
+#include "dat.h"
+#include "fns.h"
+
+static Wind *mainwind;			/* server window */
+static Irc *conn;			/* our connection */
+static char *nick;
+
+static void windevents(Wind*);
+
+static void
+windproc(void *v)
+{
+	char *name;
+	Wind *w;
+
+	name = v;
+
+	threadsetname("wind %s", name);
+
+	/* copy ns since we fiddle with the namespace for graphics */
+	rfork(RFNAMEG);
+
+	if(riowindow("-scroll") < 0)
+		sysfatal("newwindow: %r");
+
+	riolabel(name);
+
+	w = windmk(name);
+	windlink(mainwind, w);
+
+	free(name);
+
+	windevents(w);
+
+	windunlink(mainwind, w);
+	if(chanclosing(w->event) == -1)
+		chanclose(w->event);
+	windfree(w);
+
+	rioclose();
+	threadexits(nil);
+}
+
+enum
+{
+	CMexit,
+	CMquery,
+	CMnick,
+};
+
+Cmdtab inputtab[] =
+{
+	CMexit,		"x",		1,
+	CMexit,		"exit",		1,
+	CMquery,	"q",		2,
+	CMquery,	"query",	2,
+	CMnick,		"n",		2,
+	CMnick,		"nick",		2,
+};
+
+static void
+windevents(Wind *w)
+{
+	char *s, *name;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	while((s = recvp(w->event)) != nil){
+		if(s[0] == '/' && strlen(s) > 1){
+			cb = parsecmd(s+1, strlen(s+1));
+			ct = lookupcmd(cb, inputtab, nelem(inputtab));
+			if(ct == nil){
+				windappend(w, "* invalid command %q *", cb->f[0]);
+				continue;
+			}
+
+			switch(ct->index){
+			case CMexit:
+				free(cb);
+			part:
+				if(w->target != nil && w->target[0] == '#')
+					ircpart(conn, w->target);
+				goto done;
+				break;
+
+			case CMquery:
+				if(cb->f[1][0] == '#')
+					ircjoin(conn, cb->f[1]);
+
+				name = strdup(cb->f[1]);
+				proccreate(windproc, name, 8192);
+				break;
+
+			case CMnick:
+				ircnick(conn, cb->f[1]);
+				break;
+			}
+			free(cb);
+		} else {
+			if(w->target != nil){
+				ircprivmsg(conn, w->target, s);
+			} else {
+				ircraw(conn, s);
+			}
+		}
+		free(s);
+	}
+
+	/* make sure to part on rio del */
+	if(w->target != nil && w->target[0] == '#')
+		goto part;
+
+done:
+	return;
+}
+
+static void
+on433(Irc *irc, IrcMsg*, void*)
+{
+	char buf[128];
+
+	snprint(buf, sizeof buf, "%s%d", nick, 10+nrand(1000));
+	windappend(mainwind, "* nickname %q in use, switching to %q *", nick, buf);
+
+	ircnick(irc, buf);
+}
+
+// NOTICE/PRIVMSG
+static void
+msg(Irc*, IrcMsg *m, void*)
+{
+	int r;
+	char buf[512], prefix[256];
+	char *s;
+	Wind *w;
+
+	/* user privmsg */
+	strcpy(prefix, m->prefix);
+	if((s = strchr(prefix, '!')) != nil)
+		*s = 0;
+
+	snprint(buf, sizeof buf, "%s⇒ ", prefix);
+	for(r = 1; r < m->nargs; r++){
+		strcat(buf, " ");
+		strcat(buf, m->args[r]);
+	}
+
+	if(m->args[0][0] == '#'){
+		w = windfind(mainwind, m->args[0]);
+	} else {
+		w = windfind(mainwind, prefix);
+	}
+
+	if(w != nil){
+		windappend(w, buf);
+	} else {
+		windappend(mainwind, buf);
+	}
+}
+
+static int
+inset(char *needle, char **haystack){
+	char **p;
+	for(p = haystack; *p != nil; (*p)++)
+		if(cistrcmp(needle, *p) == 0)
+			return 0;
+
+	return -1;
+}
+
+static void
+catchall(Irc*, IrcMsg *m, void*)
+{
+	int i;
+	char buf[512];
+
+	if(m->cmd[0] == '_')
+		return;
+
+	char *unwanted[]={
+		IRC_PING,
+		IRC_NOTICE,
+		IRC_PRIVMSG,
+		nil,
+	};
+
+	if(inset(m->cmd, unwanted) == 0)
+		return;
+
+	snprint(buf, sizeof buf, "%s⇒ ", m->prefix);
+	for(i = 1; i < m->nargs; i++){
+		strcat(buf, " ");
+		strcat(buf, m->args[i]);
+	}
+
+	windappend(mainwind, buf);
+}
+
+static void
+ondisconnect(Irc *, IrcMsg*, void*)
+{
+	windappend(mainwind, "* disconnected *");
+}
+
+static void
+onconnect(Irc *irc, IrcMsg*, void*)
+{
+	Wind *w;
+
+	windappend(mainwind, "* connected *");
+	ircuser(irc, "none", "0", "none");
+	ircnick(irc, nick);;
+
+	qlock(mainwind);
+	for(w = mainwind; w != nil; w = w->next){
+		if(w->target != nil && w->target[0] == '#')
+			ircjoin(irc, w->target);
+	}
+	qunlock(mainwind);
+}
+
+static void
+ircproc(void *v)
+{
+	Irc *irc;
+
+	irc = v;
+
+	procsetname("irc %s", ircconf(irc)->address);
+
+	ircrun(irc);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-t] [-a address]\n", argv0);
+	threadexitsall("usage");
+}
+
+static int
+tlsdial(Irc *, IrcConf *conf)
+{
+	TLSconn c;
+	int fd;
+
+	fd = dial(conf->address, nil, nil, nil);
+	if(fd < 0)
+		return -1;
+	return tlsClient(fd, &c);
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	IrcConf conf = { 0 };
+	char *address = getenv("irc");
+
+	ARGBEGIN{
+	case 't':
+		conf.dial = tlsdial;
+		break;
+	case 'a':
+		address = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 0)
+		usage();
+
+	quotefmtinstall();
+	srand(truerand());
+
+	rfork(RFNOTEG);
+
+	if(address == nil)
+		address = "tcp!irc.oftc.net!6697";
+
+	conf.address = address;
+	nick = getuser();
+
+	riolabel(address);
+
+	mainwind = windmk(nil);
+
+	conn = ircinit(&conf);
+	irchook(conn, IRC_CONNECT, onconnect);
+	irchook(conn, IRC_DISCONNECT, ondisconnect);
+	irchook(conn, IRC_ERR_NICKNAMEINUSE, on433);
+	irchook(conn, IRC_PRIVMSG, msg);
+	irchook(conn, IRC_NOTICE, msg);
+	irchook(conn, nil, catchall);
+	
+	proccreate(ircproc, conn, 16*1024);
+
+	procsetname("main window");
+
+	windevents(mainwind);
+
+	postnote(PNGROUP, getpid(), "die yankee pig dog");
+	threadexitsall(nil);
+}
--- /dev/null
+++ b/chat/mkfile
@@ -1,0 +1,19 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=chat
+OFILES=\
+	main.$O\
+	util.$O\
+	wind.$O\
+
+LIB=/$objtype/lib/libirc.a
+
+MAN=/sys/man/1
+
+</sys/src/cmd/mkone
+
+install:V:	man
+
+uninstall:V:
+	rm -f $BIN/$TARG $MAN/$TARG
--- /dev/null
+++ b/chat/util.c
@@ -1,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+
+u64int jenkinshash(char *key, int len)
+{
+	u64int hash, i;
+	for(hash = i = 0; i < len; ++i){
+		hash += key[i];
+		hash += (hash << 10);
+		hash ^= (hash >> 6);
+	}
+	hash += (hash << 3);
+	hash ^= (hash >> 11);
+	hash += (hash << 15);
+	return hash;
+}
+
+static void
+fwrites(char *f, char *s)
+{
+	int fd;
+	fd = open(f, OWRITE);
+	if(fd > 0){
+		write(fd, s, strlen(s));
+		close(fd);
+	}
+}
+
+void
+riolabel(char *label)
+{
+	fwrites("/dev/label", label);
+}
+
+void
+rioclose(void)
+{
+	fwrites("/dev/wctl", "delete");
+}
+
+/* copied from /sys/src/libdraw/newwindow.c to avoid linking in libdraw. */
+int
+riowindow(char *str)
+{
+	int fd;
+	char *wsys;
+	char buf[256];
+
+	wsys = getenv("wsys");
+	if(wsys == nil)
+		return -1;
+	fd = open(wsys, ORDWR);
+	if(fd < 0){
+		free(wsys);
+		return -1;
+	}
+	rfork(RFNAMEG);
+	unmount(wsys, "/dev");	/* drop reference to old window */
+	free(wsys);
+	if(str)
+		snprint(buf, sizeof buf, "new %s", str);
+	else
+		strcpy(buf, "new");
+	if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0)
+		return mount(fd, -1, "/dev", MBEFORE, buf);
+	return bind("/mnt/wsys", "/dev", MBEFORE);
+}
--- /dev/null
+++ b/chat/wind.c
@@ -1,0 +1,131 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+
+#include "dat.h"
+#include "fns.h"
+
+static void
+kbdproc(void *v)
+{
+	char line[512];
+	int r;
+	Wind *w;
+
+	w = v;
+
+	threadsetname("kbdproc");
+
+	while((r = read(w->kfd, line, sizeof line - 1)) > 0){
+		if(r > 0 && line[r] == '\n')
+			r--;
+
+		line[r] = 0;
+
+		chanprint(w->event, "%s", line);
+	}
+
+	chanclose(w->event);
+}
+
+Wind*
+windmk(char *target)
+{
+	int r;
+	Wind *w;
+
+	w = mallocz(sizeof(*w), 1);
+	if(target != nil){
+		for(r = 0; r < strlen(target); r++)
+			target[r] = tolower(target[r]);
+
+		w->id = jenkinshash(target, strlen(target));
+	}
+
+	if(target != nil)
+		w->target = strdup(target);
+
+	w->event = chancreate(sizeof(char*), 0);
+	w->kpid = -1;
+
+	if((w->kfd = open("/dev/cons", ORDWR)) < 0){
+		sysfatal("open: %r");
+	}
+
+	w->kpid = proccreate(kbdproc, w, 8192);
+	return w;
+}
+
+void
+windfree(Wind *w)
+{
+	qlock(w);
+
+	if(w->kpid > 0)
+		postnote(PNPROC, w->kpid, "die yankee pig dog");
+
+	if(w->event != nil)
+		chanfree(w->event);
+
+	free(w->target);
+	free(w);
+}
+
+void
+windlink(Wind *l, Wind *w)
+{
+	qlock(l);
+	w->prev = l;
+	w->next = l->next;
+	l->next = w;
+	qunlock(l);
+}
+
+void
+windunlink(Wind *l, Wind *w)
+{
+	qlock(l);
+	if(w->next != nil)
+		w->next->prev = w->prev;
+	if(w->prev != nil)
+		w->prev->next = w->next;
+	qunlock(l);
+}
+
+Wind*
+windfind(Wind *l, char *target)
+{
+	int r;
+	u64int id;
+	Wind *w;
+
+	for(r = 0; r < strlen(target); r++)
+		target[r] = tolower(target[r]);
+
+	id = jenkinshash(target, strlen(target));
+
+	qlock(l);
+	for(w = l; w != nil; w = w->next){
+		if(w->id == id)
+			break;
+	}
+	qunlock(l);
+
+	return w;
+}
+
+void
+windappend(Wind *w, char *msg, ...)
+{
+	va_list arg;
+
+	qlock(w);
+
+	va_start(arg, msg);
+	vfprint(w->kfd, msg, arg);
+	va_end(arg);
+
+	write(w->kfd, "\n", 1);
+
+	qunlock(w);
+}
--- /dev/null
+++ b/libirc/irc.c
@@ -1,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#include "irc.h"
+
+typedef struct Hook Hook;
+struct Hook
+{
+	char what[64];
+	IrcCb *cb;
+	Hook *next;
+};
+
+struct Irc
+{
+	QLock;
+
+	IrcConf *conf;
+
+	int fd;
+	Biobuf *buf;
+	Hook *head;
+
+	int run;
+};
+
+IrcMsg*
+parsemsg(char *b)
+{
+	char *tok[5], *args[20];
+	int i, n, ntok, nargs;
+	IrcMsg *msg;
+
+	if(b == nil || b[0] == 0)
+		return nil;
+
+	msg = mallocz(sizeof(*msg), 1);
+	if(msg == nil)
+		return nil;
+
+	if(b[0] == ':'){
+		ntok = gettokens(b, tok, 2, " ");
+		if(ntok != 2)
+			goto fail;
+		strncpy(msg->prefix, tok[0]+1, sizeof(msg->prefix)-1);
+		b = tok[1];
+	}
+
+	ntok = gettokens(b, tok, 2, ":");
+
+	nargs = gettokens(tok[0], args, nelem(args), " ");
+
+	strncpy(msg->cmd, args[0], sizeof(msg->cmd)-1);
+	n = strlen(msg->cmd);
+	for(i = 0; i < n; i++){
+		msg->cmd[i] = tolower(msg->cmd[i]);
+	}
+
+	msg->nargs = nargs - 1 + (ntok > 1 ? 1 : 0);
+
+	msg->args = mallocz(msg->nargs * sizeof(char*), 1);
+	if(msg->args == nil)
+		goto fail;
+
+	for(i = 0; i < msg->nargs - (ntok > 1 ? 1 : 0); i++){
+		msg->args[i] =  strdup(args[i+1]);
+	}
+
+	if(ntok > 1){
+		msg->args[i] = strdup(tok[1]);
+	}
+
+/*
+	fprint(2, "prefix: %s\n", msg->prefix);
+	fprint(2, "command: %s\n", msg->cmd);
+	for(i = 0; i < msg->nargs; i++){
+		fprint(2, " %s\n", msg->args[i]);
+	}
+*/
+
+	return msg;
+fail:
+	free(msg);
+	return nil;
+}
+
+void
+freemsg(IrcMsg *m)
+{
+	int i;
+
+	assert(m != nil);
+
+	if(m->nargs > 0){
+		for(i = 0; i < m->nargs; i++)
+			free(m->args[i]);
+		free(m->args);
+	}
+
+	free(m);
+}
+
+void
+pinghandler(Irc *irc, IrcMsg *msg, void*)
+{
+	if(msg->nargs > 0)
+		ircrawf(irc, "PONG :%s", msg->args[0]);
+}
+
+Irc*
+ircinit(IrcConf *conf)
+{
+	Irc *c;
+
+	c = mallocz(sizeof(*c), 1);
+	if(c == nil)
+		return nil;
+
+	c->conf = conf;
+	c->fd = -1;
+	c->run = 1;
+
+	if(irchook(c, IRC_PING, pinghandler) < 0)
+		goto bad1;
+
+	return c;
+
+bad1:
+	free(c);
+	return nil;
+}
+
+void
+ircfree(Irc *irc)
+{
+	Hook *h, *next;
+
+	for(h = irc->head, next = h->next; next != nil; h = next, next = next->next){
+		free(h);
+	}
+
+	if(irc->buf)
+		Bterm(irc->buf);
+}
+
+static void
+callhook(Irc *irc, char *what, IrcMsg *msg){
+	Hook *h;
+	IrcMsg fake;
+
+	if(msg == nil){
+		msg = &fake;
+		memset(msg, 0, sizeof(*msg));
+		strcpy(msg->cmd, what);
+	}
+
+	for(h = irc->head; h != nil; h = h->next)
+		if(h->what[0] == 0 || cistrcmp(what, h->what) == 0)
+			h->cb(irc, msg, irc->conf->aux);
+}
+
+int
+ircrun(Irc *irc)
+{
+	int l;
+	char *p;
+	IrcMsg *msg;
+	Biobuf *buf;
+
+	buf = nil;
+
+	for(;;){
+		qlock(irc);
+		if(!irc->run)
+			break;
+		qunlock(irc);
+
+		switch(irc->fd){
+		case -1:
+			callhook(irc, IRC_DIALING, nil);
+
+			if(irc->conf->dial != nil)
+				irc->fd = irc->conf->dial(irc, irc->conf);
+			else
+				irc->fd = dial(irc->conf->address, nil, nil, nil);
+
+			if(irc->fd >= 0){
+				buf = Bfdopen(irc->fd, OREAD);
+				if(buf == nil){
+					callhook(irc, IRC_OOM, nil);
+					break;
+				}
+
+				callhook(irc, IRC_CONNECT, nil);
+			}
+
+			sleep(5000);
+
+			break;
+		default:
+			p = Brdstr(buf, '\n', 1);
+			if(p == nil){
+				// eof/maybe oom?
+				Bterm(buf);
+				buf = nil;
+				irc->fd = -1;
+				callhook(irc, IRC_DISCONNECT, nil);
+				break;
+			}
+
+			l = Blinelen(buf);
+			if(l > 0 && p[l-1] == '\r')
+				p[l-1] = 0;
+
+			if(irc->conf->debug)
+				fprint(2, "<< %s\n", p);
+
+			msg = parsemsg(p);
+			free(p);
+			if(msg == nil){
+				callhook(irc, IRC_OOM, nil);
+				break;
+			}
+
+			if(strcmp(msg->cmd, IRC_PING) == 0){
+				ircrawf(irc, "PONG: %s", msg->args[0]);
+			}
+
+			callhook(irc, msg->cmd, msg);
+
+			freemsg(msg);
+
+			break;
+		}
+	}
+
+	if(buf != nil)
+		Bterm(buf);
+
+	return 0;
+}
+
+int
+irchook(Irc *irc, char *what, IrcCb *cb)
+{
+	Hook *h;
+
+	h = mallocz(sizeof(*h), 1);
+	if(h == nil)
+		return -1;
+
+	if(what == nil)
+		what = "";
+
+	snprint(h->what, sizeof(h->what), "%s", what);
+	h->cb = cb;
+	h->next = irc->head;
+
+	irc->head = h;
+
+	return 0;
+}
+
+IrcConf*
+ircconf(Irc *irc)
+{
+	return irc->conf;
+}
+
+void
+ircterminate(Irc *irc)
+{
+	qlock(irc);
+	irc->run = 0;
+	close(irc->fd);
+	qunlock(irc);
+}
+
+int
+ircraw(Irc *irc, char *msg)
+{
+	int rv;
+
+	if(irc->conf->debug)
+		fprint(2, ">> %s\n", msg);
+
+	rv = write(irc->fd, msg, strlen(msg));
+	if(rv > 0)
+		rv = write(irc->fd, "\r\n", 2);
+	return rv;
+}
+
+int
+ircrawf(Irc *irc, char *fmt, ...)
+{
+	char tmp[512];
+	va_list arg;
+
+	va_start(arg, fmt);
+	
+	tmp[0] = 0;
+	vsnprint(tmp, sizeof(tmp), fmt, arg);
+
+	return ircraw(irc, tmp);
+}
+
+int
+ircquit(Irc *irc, char *why)
+{
+	return ircrawf(irc, "QUIT :%s", why);
+}
+
+int
+ircuser(Irc *irc, char *user, char *mode, char *realname)
+{
+	return ircrawf(irc, "USER %s %s * :%s", user, mode, realname);
+}
+
+int
+ircnick(Irc *irc, char *nickname)
+{
+	return ircrawf(irc, "NICK %s", nickname);
+}
+
+int
+ircjoin(Irc *irc, char *channel)
+{
+	return ircrawf(irc, "JOIN %s", channel);
+}
+
+int
+ircpart(Irc *irc, char *channel)
+{
+	return ircrawf(irc, "PART %s", channel);
+}
+
+int
+ircprivmsg(Irc *irc, char *tgt, char *msg)
+{
+	return ircrawf(irc, "PRIVMSG %s :%s", tgt, msg);
+}
--- /dev/null
+++ b/libirc/irc.h
@@ -1,0 +1,50 @@
+typedef struct IrcConf IrcConf;
+typedef struct Irc Irc;
+#pragma incomplete Irc
+typedef struct IrcMsg IrcMsg;
+typedef struct IrcHooks IrcHooks;
+
+typedef void IrcCb(Irc*, IrcMsg*, void *aux);
+
+struct IrcConf
+{
+	char 	*address;
+	int		debug;
+	void	*aux;
+	int		(*dial)(Irc *irc, IrcConf *conf);
+};
+
+struct IrcMsg
+{
+	char	prefix[256];
+	char	cmd[64];
+	char	**args;
+	int		nargs;
+};
+
+#define IRC_DISCONNECT 	"_DISCONNECTED"
+#define IRC_CONNECT		"_CONNECTED"
+#define IRC_DIALING		"_DIALING"
+#define IRC_OOM			"_OOM"
+
+#define IRC_PING		"PING"
+#define IRC_NOTICE		"NOTICE"
+#define IRC_PRIVMSG 	"PRIVMSG"
+
+#define IRC_RPL_WELCOME			"001"
+#define IRC_ERR_NICKNAMEINUSE	"433"	
+
+Irc* ircinit(IrcConf *conf);
+void ircfree(Irc*);
+int ircrun(Irc*);
+int irchook(Irc *irc, char *what, IrcCb *cb);
+void ircterminate(Irc *irc);
+IrcConf *ircconf(Irc*);
+int ircraw(Irc *irc, char *msg);
+int ircrawf(Irc *irc, char *fmt, ...);
+int ircquit(Irc *irc, char *why);
+int ircuser(Irc *irc, char *user, char *mode, char *realname);
+int ircnick(Irc *irc, char *nickname);
+int ircjoin(Irc *irc, char *channel);
+int ircpart(Irc *irc, char *channel);
+int ircprivmsg(Irc *irc, char *tgt, char *msg);
--- /dev/null
+++ b/libirc/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+P=irc
+
+LIB=lib$P.$O.a
+OFILES=$P.$O
+HFILES=/sys/include/$P.h
+
+</sys/src/cmd/mklib
+
+install:V:	$LIB
+	cp $LIB /$objtype/lib/lib$P.a
+	cp $P.h /sys/include/$P.h
+	#cp $P.man2 /sys/man/2/$P
+
+uninstall:V:
+	rm -f /$objtype/lib/lib$P.a /sys/include/$P.h /sys/man/2/$P
+
+$O.test: test.$O $LIB
+	$LD -o $target $prereq
--- /dev/null
+++ b/netsurf/layout.c
@@ -1,0 +1,221 @@
+#include <draw.h>
+#include <ctype.h>
+#include "utils/nsoption.h"
+#include "netsurf/inttypes.h"
+#include "netsurf/layout.h"
+#include "netsurf/plot_style.h"
+#include "utils/utf8.h"
+#include "plan9/layout.h"
+#include "plan9/utils.h"
+
+/* from mothra */
+struct font_definitions {
+	char *name;
+	Font *font;
+} fontlist[4][4] = {
+	"lucidasans/unicode.7", 0,
+	"lucidasans/unicode.8", 0,
+	"lucidasans/unicode.10", 0,
+	"lucidasans/unicode.13", 0,
+
+	"lucidasans/italicunicode.7", 0,
+	"lucidasans/italicunicode.8", 0,
+	"lucidasans/italicunicode.10", 0,
+	"lucidasans/italicunicode.13", 0,
+
+	"lucidasans/boldunicode.7", 0,
+	"lucidasans/boldunicode.8", 0,
+	"lucidasans/boldunicode.10", 0,
+	"lucidasans/boldunicode.13", 0,
+
+	"pelm/unicode.9", 0,
+	"pelm/unicode.9", 0,
+	"pelm/unicode.9", 0,
+	"pelm/unicode.9", 0,
+};
+
+enum
+{
+	FONT_SIZE_SMALL = 0,
+	FONT_SIZE_MEDIUM,
+	FONT_SIZE_LARGE,
+	FONT_SIZE_HUGE,
+};
+
+enum
+{
+	FONT_NORMAL = 0,
+	FONT_ITALIC,
+	FONT_BOLD,
+	FONT_MONO
+};
+
+/**
+ * \brief Convert a netsurf font description to a Plan 9 font
+ *
+ * \param fstyle The font style description
+ * \return A matching font on success or the default system font
+ */
+Font* getfont(const plot_font_style_t *fstyle)
+{
+	int s, t, size;
+	char buf[255];
+
+	size = fstyle->size / PLOT_STYLE_SCALE;
+	if(size < 9)
+		s = FONT_SIZE_SMALL;
+	else if(size < 13)
+		s = FONT_SIZE_MEDIUM;
+	else if(size < 17)
+		s = FONT_SIZE_LARGE;
+	else
+		s = FONT_SIZE_HUGE;
+
+	if(fstyle->weight >= 500)
+		t = FONT_BOLD;
+	else if(fstyle->family == PLOT_FONT_FAMILY_MONOSPACE)
+		t = FONT_MONO;
+	else if(fstyle->flags == FONTF_ITALIC || fstyle->flags == FONTF_OBLIQUE)
+		t = FONT_ITALIC;
+	else
+		t = FONT_NORMAL;
+
+	if(fontlist[t][s].font == 0) {
+		snprintf(buf, sizeof buf, "/lib/font/bit/%s.font", fontlist[t][s].name);
+		fontlist[t][s].font = openfont(display, buf);
+		if(fontlist[t][s].font==0)
+			fontlist[t][s].font=font;
+	}
+	return fontlist[t][s].font;
+}
+
+/**
+ * Measure the width of a string.
+ *
+ * \param[in] fstyle plot style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[out] width updated to width of string[0..length)
+ * \return NSERROR_OK and width updated or appropriate error
+ *          code on faliure
+ */
+nserror
+layout_width(const struct plot_font_style *fstyle, const char *s, size_t length, int *width)
+{
+	Font *f;
+        uint32_t ucs4;
+        size_t nxtchr = 0;
+
+	f = getfont(fstyle);
+	*width = 0;
+        while (nxtchr < length) {
+                ucs4 = utf8_to_ucs4(s + nxtchr, length - nxtchr);
+                nxtchr = utf8_next(s, length, nxtchr);
+
+                *width += runestringnwidth(f, &ucs4, 1);
+        }
+	return NSERROR_OK;
+}
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x coordinate to search for
+ * \param[out] char_offset updated to offset in string of actual_x, [0..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK and char_offset and actual_x updated or appropriate error code on faliure
+ */
+nserror 
+layout_position(const struct plot_font_style *fstyle, const char *s, size_t length, int x, size_t *char_offset, int *actual_x)
+{
+	Font *f;
+        uint32_t ucs4;
+        size_t nxtchr = 0;
+        int prev_x = 0;
+
+	f = getfont(fstyle);
+        *actual_x = 0;
+        while (nxtchr < length) {
+                ucs4 = utf8_to_ucs4(s + nxtchr, length - nxtchr);
+
+                *actual_x += runestringnwidth(f, &ucs4, 1);
+                if (*actual_x > x)
+                        break;
+
+                prev_x = *actual_x;
+                nxtchr = utf8_next(s, length, nxtchr);
+        }
+
+        /* choose nearest of previous and last x */
+        if (abs(*actual_x - x) > abs(prev_x - x))
+                *actual_x = prev_x;
+
+        *char_offset = nxtchr;
+	return NSERROR_OK;}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param[in] fstyle       style for this text
+ * \param[in] string       UTF-8 string to measure
+ * \param[in] length       length of string, in bytes
+ * \param[in] x            width available
+ * \param[out] char_offset updated to offset in string of actual_x, [1..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK or appropriate error code on faliure
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * \note char_offset of 0 must never be returned.
+ *
+ *   Returns:
+ *     char_offset giving split point closest to x, where actual_x <= x
+ *   else
+ *     char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+nserror
+layout_split(const struct plot_font_style *fstyle, const char *s, size_t length, int x, size_t *char_offset, int *actual_x)
+{
+	Font *f;
+	uint32_t ucs4;
+	size_t nxtchr = 0;
+	int last_space_x = 0;
+	int last_space_idx = 0;
+
+	f = getfont(fstyle);
+	*actual_x = 0;
+	while (nxtchr < length) {
+		ucs4 = utf8_to_ucs4(s + nxtchr, length - nxtchr);
+		
+		if (ucs4 == ' ' || ucs4 == '\t') {
+			last_space_x = *actual_x;
+			last_space_idx = nxtchr;
+		}
+
+		*actual_x += runestringnwidth(f, &ucs4, 1);
+		if (*actual_x > x && last_space_idx != 0) {
+			*actual_x = last_space_x;
+			*char_offset = last_space_idx;
+			return NSERROR_OK;
+		}
+		
+		nxtchr = utf8_next(s, length, nxtchr);
+	}
+	*char_offset = length;
+	return NSERROR_OK;
+}
+
+static struct gui_layout_table layout_table = {
+	.width = layout_width,
+	.position = layout_position,
+	.split = layout_split,
+};
+
+struct gui_layout_table *plan9_layout_table = &layout_table;
--- /dev/null
+++ b/pelm/unicode.10.font
@@ -1,0 +1,166 @@
+17 14
+0x0000	0x007f	ascii.10
+0xfff9	0xffff	../dejavu/dejavu.14.fff9
+0xfb00	0xfc00	../dejavu/dejavu.14.fb00
+0xf6c5	0xf7c5	../dejavu/dejavu.14.f6c5
+0xf400	0xf500	../dejavu/dejavu.14.f400
+0xa746	0xa846	../dejavu/dejavu.14.a746
+0xa644	0xa744	../dejavu/dejavu.14.a644
+0x2e18	0x2f18	../dejavu/dejavu.14.2e18
+0x2c60	0x2d60	../dejavu/dejavu.14.2c60
+0x2b00	0x2c00	../dejavu/dejavu.14.2b00
+0x29eb	0x2aeb	../dejavu/dejavu.14.29eb
+0x28a2	0x29a2	../dejavu/dejavu.14.28a2
+0x27a1	0x28a1	../dejavu/dejavu.14.27a1
+0x2524	0x2624	../dejavu/dejavu.14.2524
+0x2423	0x2523	../dejavu/dejavu.14.2423
+0x1e01	0x1f01	../dejavu/dejavu.14.1e01
+0x1d00	0x1e00	../dejavu/dejavu.14.1d00
+0x10a0	0x11a0	../dejavu/dejavu.14.10a0
+0x0101	0x0201	../dejavu/dejavu.14.0101
+0x0000	0x0100	../dejavu/dejavu.14.0000
+0xfe00	0xfeff	../dejavusans/dejavusans.14.fe00
+0xf001	0xf101	../dejavusans/dejavusans.14.f001
+0xef00	0xf000	../dejavusans/dejavusans.14.ef00
+0x4dc0	0x4ec0	../dejavusans/dejavusans.14.4dc0
+0x2d61	0x2e61	../dejavusans/dejavusans.14.2d61
+0x2104	0x21fb	../dejavusans/dejavusans.14.2104
+0x2100	0x2103	../dejavusans/dejavusans.14.2003
+0x1f02	0x2002	../dejavusans/dejavusans.14.1f02
+0x1401	0x1501	../dejavusans/dejavusans.14.1401
+0x0f00	0x0f3f	../dejavusans/dejavusans.14.0e3f
+0x0404	0x0504	../dejavusans/dejavusans.14.0404
+0x03c0	0x03c0	../dejavusans/dejavusans.14.03c0
+0x03a9	0x03a9	../dejavusans/dejavusans.14.03a9
+0x0303	0x0403	../dejavusans/dejavusans.14.0303
+0x0202	0x02ff	../dejavusans/dejavusans.14.0202
+0x3000	0x30fe	../shinonome/k14.3000
+0x4e00	0x4ffe	../shinonome/k14.4e00
+0x5005	0x51fe	../shinonome/k14.5005
+0x5200	0x53fa	../shinonome/k14.5200
+0x5401	0x55fe	../shinonome/k14.5401
+0x5606	0x57fc	../shinonome/k14.5606
+0x5800	0x59ff	../shinonome/k14.5800
+0x5a01	0x5bff	../shinonome/k14.5a01
+0x5c01	0x5dfe	../shinonome/k14.5c01
+0x5e02	0x5fff	../shinonome/k14.5e02
+0x600e	0x61ff	../shinonome/k14.600e
+0x6200	0x63fa	../shinonome/k14.6200
+0x6406	0x65fb	../shinonome/k14.6406
+0x6602	0x67ff	../shinonome/k14.6602
+0x6802	0x69ff	../shinonome/k14.6802
+0x6a02	0x6bf3	../shinonome/k14.6a02
+0x6c08	0x6dfb	../shinonome/k14.6c08
+0x6e05	0x6ffe	../shinonome/k14.6e05
+0x7001	0x71ff	../shinonome/k14.7001
+0x7206	0x73fe	../shinonome/k14.7206
+0x7403	0x75ff	../shinonome/k14.7403
+0x7601	0x77fc	../shinonome/k14.7601
+0x7802	0x79fb	../shinonome/k14.7802
+0x7a00	0x7bf7	../shinonome/k14.7a00
+0x7c00	0x7dfb	../shinonome/k14.7c00
+0x7e01	0x7ffc	../shinonome/k14.7e01
+0x8000	0x81fe	../shinonome/k14.8000
+0x8201	0x83fd	../shinonome/k14.8201
+0x8403	0x85fe	../shinonome/k14.8403
+0x8602	0x87fe	../shinonome/k14.8602
+0x8805	0x89f8	../shinonome/k14.8805
+0x8a00	0x8b9a	../shinonome/k14.8a00
+0x8c37	0x8dff	../shinonome/k14.8c37
+0x8e08	0x8ffd	../shinonome/k14.8e08
+0x9000	0x91ff	../shinonome/k14.9000
+0x920d	0x93e8	../shinonome/k14.920d
+0x9403	0x95e5	../shinonome/k14.9403
+0x961c	0x97ff	../shinonome/k14.961c
+0x9801	0x99ff	../shinonome/k14.9801
+0x9a01	0x9bf5	../shinonome/k14.9a01
+0x9c04	0x9dfd	../shinonome/k14.9c04
+0x9e1a	0x9fa0	../shinonome/k14.9e1a
+0x0500	0x05ff	../fixed/10x20.0500
+0x0600	0x06ff	../fixed/10x20.0600
+0x0e00	0x0eff	../fixed/10x20.0E00
+0x1000	0x10ff	../fixed/10x20.1000
+0x1200	0x12ff	../fixed/10x20.1200
+0x1300	0x13ff	../fixed/10x20.1300
+0x1600	0x16ff	../fixed/10x20.1600
+0x2000	0x2044	../misc/genpunc.9
+0x2070	0x208e	../pelm/supsub.9
+0x20a0	0x20ac	../pelm/currency.9
+0x2200	0x227f	../misc/math1
+0x2280	0x22f1	../misc/math2
+0x2300	0x232c	../misc/tech
+0x2600	0x266f	../misc/ding
+0x2700	0x27bf	../misc/zapf
+0x2a00	0x2aff	../fixed/10x20.2A00
+0x4d00	0x4dff	../fixed/10x20.4D00
+0x4e00	0x4fff	../jis/jis4e00.16
+0x5000	0x51ff	../jis/jis5000.16
+0x5200	0x53ff	../jis/jis5200.16
+0x5400	0x55ff	../jis/jis5400.16
+0x5600	0x57ff	../jis/jis5600.16
+0x5a00	0x5bff	../jis/jis5a00.16
+0x5c00	0x5dff	../jis/jis5c00.16
+0x5e00	0x5fff	../jis/jis5e00.16
+0x6000	0x61ff	../jis/jis6000.16
+0x6200	0x63ff	../jis/jis6200.16
+0x6400	0x65ff	../jis/jis6400.16
+0x6600	0x67ff	../jis/jis6600.16
+0x6800	0x69ff	../jis/jis6800.16
+0x6a00	0x6bff	../jis/jis6a00.16
+0x6c00	0x6dff	../jis/jis6c00.16
+0x6e00	0x6fff	../jis/jis6e00.16
+0x7000	0x71ff	../jis/jis7000.16
+0x7200	0x73ff	../jis/jis7200.16
+0x7400	0x75ff	../jis/jis7400.16
+0x7600	0x77ff	../jis/jis7600.16
+0x7800	0x79ff	../jis/jis7800.16
+0x7a00	0x7bff	../jis/jis7a00.16
+0x7c00	0x7dff	../jis/jis7c00.16
+0x7e00	0x7fff	../jis/jis7e00.16
+0x8000	0x81ff	../jis/jis8000.16
+0x8200	0x83ff	../jis/jis8200.16
+0x8400	0x85ff	../jis/jis8400.16
+0x8600	0x87ff	../jis/jis8600.16
+0x8800	0x89ff	../jis/jis8800.16
+0x8a00	0x8bff	../jis/jis8a00.16
+0x8c00	0x8dff	../jis/jis8c00.16
+0x8e00	0x8fff	../jis/jis8e00.16
+0x9200	0x93ff	../jis/jis9200.16
+0x9400	0x95ff	../jis/jis9400.16
+0x9600	0x97ff	../jis/jis9600.16
+0x9800	0x99ff	../jis/jis9800.16
+0x9a00	0x9bff	../jis/jis9a00.16
+0x9c00	0x9dff	../jis/jis9c00.16
+0x9e00	0x9fff	../jis/jis9e00.16
+0xfc00	0xfcff	../fixed/10x20.FC00
+0xfd00	0xfdff	../fixed/10x20.FD00
+0xfee0	0xff5e	../pelm/latin1.9
+0x0300	0x03ff	../fixed/10x20.0300
+0x2000	0x20ff	../fixed/10x20.2000
+0x2100	0x21ff	../fixed/10x20.2100
+0x2200	0x22ff	../fixed/10x20.2200
+0x2300	0x23ff	../fixed/10x20.2300
+0x2400	0x24ff	../fixed/10x20.2400
+0x2600	0x26ff	../fixed/10x20.2600
+0xff00	0xffff	../fixed/10x20.FF00
+0x1400	0x14ff	../fixed/9x18.1400
+0x1500	0x15ff	../fixed/9x18.1500
+0x2940	0x2a40	../dejavusans/dejavusans.12.2940
+0x07c0	0x08c0	../dejavusans/dejavusans.12.07c0
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0x1100	0x11ff	../fixed/6x13.1100
+0xf6c4	0xf7c4	../dejavubi/dejavubi.14.f6c4
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.14.f5c5
+0xf101	0xf201	../germgoth/germgoth.16.f101
+0x3000	0x30ff	../fixed/10x20.3000
+0x1700	0x1746	../dejavusansit/dejavusansit.12.1646
+0x3130	0x318f	../source/hansans.10.3130-318f
+0xac00	0xb3ff	../source/hansans.10.ac00-b3ff
+0xb400	0xbbff	../source/hansans.10.b400-bbff
+0xbc00	0xc3ff	../source/hansans.10.bc00-c3ff
+0xc400	0xcbff	../source/hansans.10.c400-cbff
+0xcc00	0xd3ff	../source/hansans.10.cc00-d3ff
+0xd400	0xd7a3	../source/hansans.10.d400-d7a3
+0x00a0	0x021f	../vga/vga.00A0-021F
--- /dev/null
+++ b/pelm/unicode.12.font
@@ -1,0 +1,154 @@
+22 18
+0x0000	0x007f	ascii.12
+0xfff9	0xffff	../dejavu/dejavu.18.fff9
+0xfb00	0xfc00	../dejavu/dejavu.18.fb00
+0xf6c5	0xf7c5	../dejavu/dejavu.18.f6c5
+0xf400	0xf500	../dejavu/dejavu.18.f400
+0xa746	0xa846	../dejavu/dejavu.18.a746
+0xa644	0xa744	../dejavu/dejavu.18.a644
+0x2e18	0x2f18	../dejavu/dejavu.18.2e18
+0x2c60	0x2d60	../dejavu/dejavu.18.2c60
+0x2b00	0x2c00	../dejavu/dejavu.18.2b00
+0x28a2	0x29a2	../dejavu/dejavu.18.28a2
+0x27a1	0x28a1	../dejavu/dejavu.18.27a1
+0x1e01	0x1f01	../dejavu/dejavu.18.1e01
+0x1d00	0x1e00	../dejavu/dejavu.18.1d00
+0x10a0	0x11a0	../dejavu/dejavu.18.10a0
+0x0101	0x0201	../dejavu/dejavu.18.0101
+0x0000	0x0100	../dejavu/dejavu.18.0000
+0xfe00	0xfeff	../dejavusans/dejavusans.18.fe00
+0xf001	0xf101	../dejavusans/dejavusans.18.f001
+0xef00	0xf000	../dejavusans/dejavusans.18.ef00
+0x4dc0	0x4ec0	../dejavusans/dejavusans.18.4dc0
+0x2d61	0x2e61	../dejavusans/dejavusans.18.2d61
+0x1401	0x1501	../dejavusans/dejavusans.18.1401
+0x0f00	0x0f3f	../dejavusans/dejavusans.18.0e3f
+0x07c0	0x08c0	../dejavusans/dejavusans.18.07c0
+0x03c0	0x03c0	../dejavusans/dejavusans.18.03c0
+0x03a9	0x03a9	../dejavusans/dejavusans.18.03a9
+0x3000	0x30fe	../shinonome/k16.3000
+0x4e00	0x4ffe	../shinonome/k16.4e00
+0x5005	0x51fe	../shinonome/k16.5005
+0x5200	0x53fa	../shinonome/k16.5200
+0x5401	0x55fe	../shinonome/k16.5401
+0x5606	0x57fc	../shinonome/k16.5606
+0x5800	0x59ff	../shinonome/k16.5800
+0x5a01	0x5bff	../shinonome/k16.5a01
+0x5c01	0x5dfe	../shinonome/k16.5c01
+0x5e02	0x5fff	../shinonome/k16.5e02
+0x600e	0x61ff	../shinonome/k16.600e
+0x6200	0x63fa	../shinonome/k16.6200
+0x6406	0x65fb	../shinonome/k16.6406
+0x6602	0x67ff	../shinonome/k16.6602
+0x6802	0x69ff	../shinonome/k16.6802
+0x6a02	0x6bf3	../shinonome/k16.6a02
+0x6c08	0x6dfb	../shinonome/k16.6c08
+0x6e05	0x6ffe	../shinonome/k16.6e05
+0x7001	0x71ff	../shinonome/k16.7001
+0x7206	0x73fe	../shinonome/k16.7206
+0x7403	0x75ff	../shinonome/k16.7403
+0x7601	0x77fc	../shinonome/k16.7601
+0x7802	0x79fb	../shinonome/k16.7802
+0x7a00	0x7bf7	../shinonome/k16.7a00
+0x7c00	0x7dfb	../shinonome/k16.7c00
+0x7e01	0x7ffc	../shinonome/k16.7e01
+0x8000	0x81fe	../shinonome/k16.8000
+0x8201	0x83fd	../shinonome/k16.8201
+0x8403	0x85fe	../shinonome/k16.8403
+0x8602	0x87fe	../shinonome/k16.8602
+0x8805	0x89f8	../shinonome/k16.8805
+0x8a00	0x8b9a	../shinonome/k16.8a00
+0x8c37	0x8dff	../shinonome/k16.8c37
+0x8e08	0x8ffd	../shinonome/k16.8e08
+0x9000	0x91ff	../shinonome/k16.9000
+0x920d	0x93e8	../shinonome/k16.920d
+0x9403	0x95e5	../shinonome/k16.9403
+0x961c	0x97ff	../shinonome/k16.961c
+0x9801	0x99ff	../shinonome/k16.9801
+0x9a01	0x9bf5	../shinonome/k16.9a01
+0x9c04	0x9dfd	../shinonome/k16.9c04
+0x9e1a	0x9fa0	../shinonome/k16.9e1a
+0x4e00	0x4fff	../jis/jis4e00.24
+0x5000	0x51ff	../jis/jis5000.24
+0x5200	0x53ff	../jis/jis5200.24
+0x5400	0x55ff	../jis/jis5400.24
+0x5600	0x57ff	../jis/jis5600.24
+0x5a00	0x5bff	../jis/jis5a00.24
+0x5c00	0x5dff	../jis/jis5c00.24
+0x5e00	0x5fff	../jis/jis5e00.24
+0x6000	0x61ff	../jis/jis6000.24
+0x6200	0x63ff	../jis/jis6200.24
+0x6400	0x65ff	../jis/jis6400.24
+0x6600	0x67ff	../jis/jis6600.24
+0x6800	0x69ff	../jis/jis6800.24
+0x6a00	0x6bff	../jis/jis6a00.24
+0x6c00	0x6dff	../jis/jis6c00.24
+0x6e00	0x6fff	../jis/jis6e00.24
+0x7000	0x71ff	../jis/jis7000.24
+0x7200	0x73ff	../jis/jis7200.24
+0x7400	0x75ff	../jis/jis7400.24
+0x7600	0x77ff	../jis/jis7600.24
+0x7800	0x79ff	../jis/jis7800.24
+0x7a00	0x7bff	../jis/jis7a00.24
+0x7c00	0x7dff	../jis/jis7c00.24
+0x7e00	0x7fff	../jis/jis7e00.24
+0x8000	0x81ff	../jis/jis8000.24
+0x8200	0x83ff	../jis/jis8200.24
+0x8400	0x85ff	../jis/jis8400.24
+0x8600	0x87ff	../jis/jis8600.24
+0x8800	0x89ff	../jis/jis8800.24
+0x8a00	0x8bff	../jis/jis8a00.24
+0x8c00	0x8dff	../jis/jis8c00.24
+0x8e00	0x8fff	../jis/jis8e00.24
+0x9200	0x93ff	../jis/jis9200.24
+0x9400	0x95ff	../jis/jis9400.24
+0x9600	0x97ff	../jis/jis9600.24
+0x9800	0x99ff	../jis/jis9800.24
+0x9a00	0x9bff	../jis/jis9a00.24
+0x9c00	0x9dff	../jis/jis9c00.24
+0x9e00	0x9fff	../jis/jis9e00.24
+0x0200	0x02ff	../fixed/6x10.0200
+0x0300	0x03ff	../fixed/6x10.0300
+0x0400	0x04ff	../fixed/6x10.0400
+0x0500	0x05ff	../fixed/6x10.0500
+0x1600	0x16ff	../fixed/6x10.1600
+0x1f00	0x1fff	../fixed/6x10.1F00
+0x2000	0x20ff	../fixed/6x10.2000
+0x2100	0x21ff	../fixed/6x10.2100
+0x2200	0x22ff	../fixed/6x10.2200
+0x2300	0x23ff	../fixed/6x10.2300
+0x2400	0x24ff	../fixed/6x10.2400
+0x2500	0x25ff	../fixed/6x10.2500
+0x2600	0x26ff	../fixed/6x10.2600
+0xff00	0xffff	../fixed/6x10.FF00
+0x2700	0x27bf	../misc/zapf
+0x0600	0x06ff	../fixed/10x20.0600
+0x0e00	0x0eff	../fixed/10x20.0E00
+0x1000	0x10ff	../fixed/10x20.1000
+0x1200	0x12ff	../fixed/10x20.1200
+0x1300	0x13ff	../fixed/10x20.1300
+0x2a00	0x2aff	../fixed/10x20.2A00
+0x4d00	0x4dff	../fixed/10x20.4D00
+0xfc00	0xfcff	../fixed/10x20.FC00
+0xfd00	0xfdff	../fixed/10x20.FD00
+0x29eb	0x2aeb	../dejavu/dejavu.14.29eb
+0x1400	0x14ff	../fixed/9x18.1400
+0x1500	0x15ff	../fixed/9x18.1500
+0x2940	0x2a40	../dejavusans/dejavusans.12.2940
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0x1100	0x11ff	../fixed/6x13.1100
+0xf6c4	0xf7c4	../dejavubi/dejavubi.18.f6c4
+0x1700	0x1746	../dejavusansbd/dejavusansbd.18.1646
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.18.f5c5
+0xf101	0xf201	../germgoth/germgoth.18.f101
+0x3000	0x30ff	../fixed/10x20.3000
+0x3130	0x318f	../source/hansans.10.3130-318f
+0xac00	0xb3ff	../source/hansans.10.ac00-b3ff
+0xb400	0xbbff	../source/hansans.10.b400-bbff
+0xbc00	0xc3ff	../source/hansans.10.bc00-c3ff
+0xc400	0xcbff	../source/hansans.10.c400-cbff
+0xcc00	0xd3ff	../source/hansans.10.cc00-d3ff
+0xd400	0xd7a3	../source/hansans.10.d400-d7a3
+0x00a0	0x021f	../vga/vga.00A0-021F
--- /dev/null
+++ b/pelm/unicode.16.font
@@ -1,0 +1,117 @@
+30 23
+0x0000	0x007f	ascii.16
+0x0100	0x01f0	../lucida/EuroLatin.14.0
+0x2000	0x20aa	../lucida/GenPunct.14.0
+0x2200	0x22f1	../lucida/MathOps1.14.0
+0x3000	0x303f	../jis/jis3000.24
+0x30a1	0x30fe	../jis/katakana.24
+0x3041	0x309e	../jis/hiragana.24
+0x4e00	0x4fff	../jis/jis4e00.24
+0x5000	0x51ff	../jis/jis5000.24
+0x5200	0x53ff	../jis/jis5200.24
+0x5400	0x55ff	../jis/jis5400.24
+0x5600	0x57ff	../jis/jis5600.24
+0x5800	0x59ff	../jis/jis5800.24
+0x5a00	0x5bff	../jis/jis5a00.24
+0x5c00	0x5dff	../jis/jis5c00.24
+0x5e00	0x5fff	../jis/jis5e00.24
+0x6000	0x61ff	../jis/jis6000.24
+0x6200	0x63ff	../jis/jis6200.24
+0x6400	0x65ff	../jis/jis6400.24
+0x6600	0x67ff	../jis/jis6600.24
+0x6800	0x69ff	../jis/jis6800.24
+0x6a00	0x6bff	../jis/jis6a00.24
+0x6c00	0x6dff	../jis/jis6c00.24
+0x6e00	0x6fff	../jis/jis6e00.24
+0x7000	0x71ff	../jis/jis7000.24
+0x7200	0x73ff	../jis/jis7200.24
+0x7400	0x75ff	../jis/jis7400.24
+0x7600	0x77ff	../jis/jis7600.24
+0x7800	0x79ff	../jis/jis7800.24
+0x7a00	0x7bff	../jis/jis7a00.24
+0x7c00	0x7dff	../jis/jis7c00.24
+0x7e00	0x7fff	../jis/jis7e00.24
+0x8000	0x81ff	../jis/jis8000.24
+0x8200	0x83ff	../jis/jis8200.24
+0x8400	0x85ff	../jis/jis8400.24
+0x8600	0x87ff	../jis/jis8600.24
+0x8800	0x89ff	../jis/jis8800.24
+0x8a00	0x8bff	../jis/jis8a00.24
+0x8c00	0x8dff	../jis/jis8c00.24
+0x8e00	0x8fff	../jis/jis8e00.24
+0x9000	0x91ff	../jis/jis9000.24
+0x9200	0x93ff	../jis/jis9200.24
+0x9400	0x95ff	../jis/jis9400.24
+0x9600	0x97ff	../jis/jis9600.24
+0x9800	0x99ff	../jis/jis9800.24
+0x9a00	0x9bff	../jis/jis9a00.24
+0x9c00	0x9dff	../jis/jis9c00.24
+0x9e00	0x9fff	../jis/jis9e00.24
+0x0000	0x00ff	../fixed/9x18.0000
+0x0100	0x01ff	../fixed/9x18.0100
+0x0200	0x02ff	../fixed/9x18.0200
+0x0300	0x03ff	../fixed/9x18.0300
+0x0400	0x04ff	../fixed/9x18.0400
+0x0500	0x05ff	../fixed/9x18.0500
+0x0e00	0x0eff	../fixed/9x18.0E00
+0x1000	0x10ff	../fixed/9x18.1000
+0x1200	0x12ff	../fixed/9x18.1200
+0x1300	0x13ff	../fixed/9x18.1300
+0x1400	0x14ff	../fixed/9x18.1400
+0x1500	0x15ff	../fixed/9x18.1500
+0x1600	0x16ff	../fixed/9x18.1600
+0x1e00	0x1eff	../fixed/9x18.1E00
+0x1f00	0x1fff	../fixed/9x18.1F00
+0x2000	0x20ff	../fixed/9x18.2000
+0x2100	0x21ff	../fixed/9x18.2100
+0x2200	0x22ff	../fixed/9x18.2200
+0x2300	0x23ff	../fixed/9x18.2300
+0x2400	0x24ff	../fixed/9x18.2400
+0x2500	0x25ff	../fixed/9x18.2500
+0x2600	0x26ff	../fixed/9x18.2600
+0x2700	0x27ff	../fixed/9x18.2700
+0x2800	0x28ff	../fixed/9x18.2800
+0x2a00	0x2aff	../fixed/9x18.2A00
+0x3000	0x30fe	../shinonome/k14.3000
+0xfb00	0xfbff	../fixed/9x18.FB00
+0xfe00	0xfeff	../fixed/9x18.FE00
+0xff00	0xffff	../fixed/9x18.FF00
+0xfb00	0xfc00	../dejavu/dejavu.12.fb00
+0xf6c5	0xf7c5	../dejavu/dejavu.12.f6c5
+0xf400	0xf500	../dejavu/dejavu.12.f400
+0xa746	0xa846	../dejavu/dejavu.12.a746
+0xa644	0xa744	../dejavu/dejavu.12.a644
+0x2e18	0x2f18	../dejavu/dejavu.12.2e18
+0x2c60	0x2d60	../dejavu/dejavu.12.2c60
+0x2b00	0x2c00	../dejavu/dejavu.12.2b00
+0x29eb	0x2aeb	../dejavu/dejavu.12.29eb
+0x28a2	0x29a2	../dejavu/dejavu.12.28a2
+0x1d00	0x1e00	../dejavu/dejavu.12.1d00
+0x10a0	0x11a0	../dejavu/dejavu.12.10a0
+0x0510	0x0610	../dejavu/dejavu.12.0510
+0xf001	0xf101	../dejavusans/dejavusans.12.f001
+0xef00	0xf000	../dejavusans/dejavusans.12.ef00
+0x4dc0	0x4ec0	../dejavusans/dejavusans.12.4dc0
+0x2d61	0x2e61	../dejavusans/dejavusans.12.2d61
+0x2940	0x2a40	../dejavusans/dejavusans.12.2940
+0x0f00	0x0f3f	../dejavusans/dejavusans.12.0e3f
+0x07c0	0x08c0	../dejavusans/dejavusans.12.07c0
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0xfc00	0xfcff	../fixed/10x20.FC00
+0xfd00	0xfdff	../fixed/10x20.FD00
+0x1100	0x11ff	../fixed/6x13.1100
+0x4d00	0x4dff	../fixed/10x20.4D00
+0xf6c4	0xf7c4	../dejavubi/dejavubi.12.f6c4
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.12.f5c5
+0x1700	0x1746	../dejavusansit/dejavusansit.12.1646
+0xf101	0xf201	../germgoth/germgoth.14.f101
+0x3000	0x30ff	../fixed/10x20.3000
+0x3130	0x318f	../source/hansans.10.3130-318f
+0xac00	0xb3ff	../source/hansans.10.ac00-b3ff
+0xb400	0xbbff	../source/hansans.10.b400-bbff
+0xbc00	0xc3ff	../source/hansans.10.bc00-c3ff
+0xc400	0xcbff	../source/hansans.10.c400-cbff
+0xcc00	0xd3ff	../source/hansans.10.cc00-d3ff
+0xd400	0xd7a3	../source/hansans.10.d400-d7a3
--