From: Kristaps Dzonsons <kristaps@bsd.lv>
Date: Mon, 23 Feb 2009 07:09:13 +0000 (+0000)
Subject: termact.c -> term.c
X-Git-Tag: VERSION_1_3_5~24
X-Git-Url: https://git.cameronkatri.com/mandoc.git/commitdiff_plain/7caa51ee5a0c6624eb97d1d6bf06b84bdbd7f034

termact.c -> term.c
term.c -> mdocterm.c
tree.c -> mdoctree.c
Fixed/finished mmain.h.
---

diff --git a/Makefile b/Makefile
index 9277ff59..1ef89465 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,9 @@ CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -g
 LIBLNS	= macro.ln mdoc.ln hash.ln strings.ln xstd.ln argv.ln \
 	  validate.ln action.ln 
 
-TREELNS	= mdoctree.ln mmain.ln tree.ln 
+TREELNS	= mdoctree.ln mmain.ln 
 
-TERMLNS	= mdoctree.ln mmain.ln term.ln termact.ln
+TERMLNS	= mdoctree.ln mmain.ln term.ln
 
 LNS	= $(LIBLNS) $(TREELNS) $(TERMLNS)
 
@@ -18,15 +18,14 @@ LIBS	= libmdoc.a
 LIBOBJS	= macro.o mdoc.o hash.o strings.o xstd.o argv.o \
 	  validate.o action.o
 
-TERMOBJS= mdocterm.o mmain.o term.o termact.o
+TERMOBJS= mdocterm.o mmain.o term.o
 
-TREEOBJS= mdoctree.o mmain.o tree.o 
+TREEOBJS= mdoctree.o mmain.o
 
 OBJS	= $(LIBOBJS) $(TERMOBJS) $(TREEOBJS)
 
 SRCS	= macro.c mdoc.c hash.c strings.c xstd.c argv.c validate.c \
-	  action.c term.c tree.c termact.c mdoctree.c mdocterm.c \
-	  mmain.c
+	  action.c term.c mdoctree.c mdocterm.c mmain.c
 
 HEADS	= mdoc.h private.h term.h mmain.h
 
@@ -140,15 +139,9 @@ uninstall:
 macro.ln: macro.c private.h
 macro.o: macro.c private.h
 
-tree.ln: tree.c mdoc.h
-tree.o: tree.c mdoc.h
-
 term.ln: term.c term.h 
 term.o: term.c term.h
 
-termact.ln: termact.c term.h 
-termact.o: termact.c term.h
-
 strings.ln: strings.c private.h
 strings.o: strings.c private.h
 
diff --git a/mdocterm.c b/mdocterm.c
index 58350fec..faa06717 100644
--- a/mdocterm.c
+++ b/mdocterm.c
@@ -1,4 +1,4 @@
-	/* $Id: mdocterm.c,v 1.2 2009/02/22 22:58:39 kristaps Exp $ */
+	/* $Id: mdocterm.c,v 1.3 2009/02/23 07:09:13 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -17,44 +17,538 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 #include <assert.h>
+#include <ctype.h>
 #include <err.h>
 #include <getopt.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "mmain.h"
 #include "term.h"
 
+enum	termstyle {
+	STYLE_CLEAR,
+	STYLE_BOLD,
+	STYLE_UNDERLINE
+};
+
+static	void		  body(struct termp *,
+				const struct mdoc_meta *,
+				const struct mdoc_node *);
+static	void		  header(struct termp *,
+				const struct mdoc_meta *);
+static	void		  footer(struct termp *,
+				const struct mdoc_meta *);
+
+static	void		  pword(struct termp *, const char *, size_t);
+static	void		  pescape(struct termp *, 
+				const char *, size_t *, size_t);
+static	void		  chara(struct termp *, char);
+static	void		  style(struct termp *, enum termstyle);
+
+#ifdef __linux__
+extern	size_t		  strlcat(char *, const char *, size_t);
+extern	size_t		  strlcpy(char *, const char *, size_t);
+#endif
+
+
 int
 main(int argc, char *argv[])
 {
 	struct mmain	*p;
 	const struct mdoc *mdoc;
-	int		 c;
+	struct termp	 termp;
 
 	extern int	 optreset;
 	extern int	 optind;
 
 	p = mmain_alloc();
 
-	if ( ! mmain_getopt(p, argc, argv, ""))
+	if ( ! mmain_getopt(p, argc, argv, NULL, NULL, NULL, NULL))
 		mmain_exit(p, 1);
 
-	optreset = optind = 1;
-
-	while (-1 != (c = getopt(argc, argv, "")))
-		switch (c) {
-		default:
-			mmain_usage("");
-			mmain_exit(p, 1);
-			/* NOTREACHED */
-		}
-
 	if (NULL == (mdoc = mmain_mdoc(p)))
 		mmain_exit(p, 1);
 
-	termprint(mdoc_node(mdoc), mdoc_meta(mdoc));
+	termp.maxrmargin = 80; /* XXX */
+	termp.rmargin = termp.maxrmargin;
+	termp.maxcols = 1024;
+	termp.offset = termp.col = 0;
+	termp.flags = TERMP_NOSPACE;
+
+	if (NULL == (termp.buf = malloc(termp.maxcols)))
+		err(1, "malloc");
+
+	header(&termp, mdoc_meta(mdoc));
+	body(&termp, mdoc_meta(mdoc), mdoc_node(mdoc));
+	footer(&termp, mdoc_meta(mdoc));
+
+	free(termp.buf);
+
 	mmain_exit(p, 0);
 	/* NOTREACHED */
 }
 
 
+void
+flushln(struct termp *p)
+{
+	size_t		 i, j, vsz, vis, maxvis;
+
+	/*
+	 * First, establish the maximum columns of "visible" content.
+	 * This is usually the difference between the right-margin and
+	 * an indentation, but can be, for tagged lists or columns, a
+	 * small set of values.
+	 */
+
+	assert(p->offset < p->rmargin);
+	maxvis = p->rmargin - p->offset;
+	vis = 0;
+
+	/*
+	 * If in the standard case (left-justified), then begin with our
+	 * indentation, otherwise (columns, etc.) just start spitting
+	 * out text.
+	 */
+
+	if ( ! (p->flags & TERMP_NOLPAD))
+		/* LINTED */
+		for (j = 0; j < p->offset; j++)
+			putchar(' ');
+
+	/*
+	 * If we're literal, print out verbatim.
+	 */
+	if (p->flags & TERMP_LITERAL) {
+		/* FIXME: count non-printing chars. */
+		for (i = 0; i < p->col; i++)
+			putchar(p->buf[i]);
+		putchar('\n');
+		p->col = 0;
+		return;
+	}
+
+	for (i = 0; i < p->col; i++) {
+		/*
+		 * Count up visible word characters.  Control sequences
+		 * (starting with the CSI) aren't counted. 
+		 */
+		assert( ! isspace(p->buf[i]));
+
+		/* LINTED */
+		for (j = i, vsz = 0; j < p->col; j++) {
+			if (isspace(p->buf[j]))
+				break;
+			else if (27 == p->buf[j]) {
+				assert(j + 4 <= p->col);
+				j += 3;
+			} else
+				vsz++;
+		}
+		assert(vsz > 0);
+
+		/*
+		 * If a word is too long and we're within a line, put it
+		 * on the next line.  Puke if we're being asked to write
+		 * something that will exceed the right margin (i.e.,
+		 * from a fresh line or when we're not allowed to break
+		 * the line with TERMP_NOBREAK).
+		 */
+
+		if (vis && vis + vsz >= maxvis) {
+			/* FIXME */
+			if (p->flags & TERMP_NOBREAK)
+				errx(1, "word breaks right margin");
+			putchar('\n');
+			for (j = 0; j < p->offset; j++)
+				putchar(' ');
+			vis = 0;
+		} else if (vis + vsz >= maxvis) {
+			/* FIXME */
+			errx(1, "word breaks right margin");
+		}
+
+		/* 
+		 * Write out the word and a trailing space.  Omit the
+		 * space if we're the last word in the line.
+		 */
+
+		for ( ; i < p->col; i++) {
+			if (isspace(p->buf[i]))
+				break;
+			putchar(p->buf[i]);
+		}
+		vis += vsz;
+		if (i < p->col) {
+			putchar(' ');
+			vis++;
+		}
+	}
+
+	/*
+	 * If we're not to right-marginalise it (newline), then instead
+	 * pad to the right margin and stay off.
+	 */
+
+	if (p->flags & TERMP_NOBREAK) {
+		for ( ; vis <= maxvis; vis++)
+			putchar(' ');
+	} else
+		putchar('\n');
+
+	p->col = 0;
+}
+
+
+void
+newln(struct termp *p)
+{
+
+	/* 
+	 * A newline only breaks an existing line; it won't assert
+	 * vertical space.
+	 */
+	p->flags |= TERMP_NOSPACE;
+	if (0 == p->col) 
+		return;
+	flushln(p);
+}
+
+
+void
+vspace(struct termp *p)
+{
+
+	/*
+	 * Asserts a vertical space (a full, empty line-break between
+	 * lines).
+	 */
+	newln(p);
+	putchar('\n');
+}
+
+
+static void
+chara(struct termp *p, char c)
+{
+
+	/* TODO: dynamically expand the buffer. */
+	if (p->col + 1 >= p->maxcols)
+		errx(1, "line overrun");
+	p->buf[(p->col)++] = c;
+}
+
+
+static void
+style(struct termp *p, enum termstyle esc)
+{
+
+	if (p->col + 4 >= p->maxcols)
+		errx(1, "line overrun");
+
+	p->buf[(p->col)++] = 27;
+	p->buf[(p->col)++] = '[';
+	switch (esc) {
+	case (STYLE_CLEAR):
+		p->buf[(p->col)++] = '0';
+		break;
+	case (STYLE_BOLD):
+		p->buf[(p->col)++] = '1';
+		break;
+	case (STYLE_UNDERLINE):
+		p->buf[(p->col)++] = '4';
+		break;
+	default:
+		abort();
+		/* NOTREACHED */
+	}
+	p->buf[(p->col)++] = 'm';
+}
+
+
+static void
+pescape(struct termp *p, const char *word, size_t *i, size_t len)
+{
+
+	(*i)++;
+	assert(*i < len);
+
+	if ('(' == word[*i]) {
+		/* Two-character escapes. */
+		(*i)++;
+		assert(*i + 1 < len);
+
+		if ('r' == word[*i] && 'B' == word[*i + 1])
+			chara(p, ']');
+		else if ('l' == word[*i] && 'B' == word[*i + 1])
+			chara(p, '[');
+
+		(*i)++;
+		return;
+
+	} else if ('[' != word[*i]) {
+		/* One-character escapes. */
+		switch (word[*i]) {
+		case ('\\'):
+			/* FALLTHROUGH */
+		case ('\''):
+			/* FALLTHROUGH */
+		case ('`'):
+			/* FALLTHROUGH */
+		case ('-'):
+			/* FALLTHROUGH */
+		case ('.'):
+			chara(p, word[*i]);
+		default:
+			break;
+		}
+		return;
+	}
+	/* n-character escapes. */
+}
+
+
+static void
+pword(struct termp *p, const char *word, size_t len)
+{
+	size_t		 i;
+
+	/*assert(len > 0);*/ /* Can be, if literal. */
+
+	if ( ! (p->flags & TERMP_NOSPACE) && 
+			! (p->flags & TERMP_LITERAL))
+		chara(p, ' ');
+
+	p->flags &= ~TERMP_NOSPACE;
+
+	if (p->flags & TERMP_BOLD)
+		style(p, STYLE_BOLD);
+	if (p->flags & TERMP_UNDERLINE)
+		style(p, STYLE_UNDERLINE);
+
+	for (i = 0; i < len; i++) {
+		if ('\\' == word[i]) {
+			pescape(p, word, &i, len);
+			continue;
+		}
+		chara(p, word[i]);
+	}
+
+	if (p->flags & TERMP_BOLD ||
+			p->flags & TERMP_UNDERLINE)
+		style(p, STYLE_CLEAR);
+}
+
+
+void
+word(struct termp *p, const char *word)
+{
+	size_t 		 i, j, len;
+
+	if (p->flags & TERMP_LITERAL) {
+		pword(p, word, strlen(word));
+		return;
+	}
+
+	len = strlen(word);
+	assert(len > 0);
+
+	if (mdoc_isdelim(word)) {
+		if ( ! (p->flags & TERMP_IGNDELIM))
+			p->flags |= TERMP_NOSPACE;
+		p->flags &= ~TERMP_IGNDELIM;
+	}
+
+	/* LINTED */
+	for (j = i = 0; i < len; i++) {
+		if ( ! isspace(word[i])) {
+			j++;
+			continue;
+		}
+		if (0 == j)
+			continue;
+		assert(i >= j);
+		pword(p, &word[i - j], j);
+		j = 0;
+	}
+	if (j > 0) {
+		assert(i >= j);
+		pword(p, &word[i - j], j);
+	}
+}
+
+
+static void
+body(struct termp *p, const struct mdoc_meta *meta,
+		const struct mdoc_node *node)
+{
+	int		 dochild;
+
+	/* Pre-processing. */
+
+	dochild = 1;
+
+	if (MDOC_TEXT != node->type) {
+		if (termacts[node->tok].pre)
+			if ( ! (*termacts[node->tok].pre)(p, meta, node))
+				dochild = 0;
+	} else /* MDOC_TEXT == node->type */
+		word(p, node->data.text.string);
+
+	/* Children. */
+
+	if (dochild && node->child)
+		body(p, meta, node->child);
+
+	/* Post-processing. */
+
+	if (MDOC_TEXT != node->type)
+		if (termacts[node->tok].post)
+			(*termacts[node->tok].post)(p, meta, node);
+
+	/* Siblings. */
+
+	if (node->next)
+		body(p, meta, node->next);
+}
+
+
+static void
+footer(struct termp *p, const struct mdoc_meta *meta)
+{
+	struct tm	*tm;
+	char		*buf, *os;
+	size_t		 sz, osz, ssz, i;
+
+	if (NULL == (buf = malloc(p->rmargin)))
+		err(1, "malloc");
+	if (NULL == (os = malloc(p->rmargin)))
+		err(1, "malloc");
+
+	tm = localtime(&meta->date);
+
+#ifdef __linux__
+	if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
+#else
+	if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
+#endif
+		err(1, "strftime");
+
+	osz = strlcpy(os, meta->os, p->rmargin);
+
+	sz = strlen(buf);
+	ssz = sz + osz + 1;
+
+	if (ssz > p->rmargin) {
+		ssz -= p->rmargin;
+		assert(ssz <= osz);
+		os[osz - ssz] = 0;
+		ssz = 1;
+	} else
+		ssz = p->rmargin - ssz + 1;
+
+	printf("\n");
+	printf("%s", os);
+	for (i = 0; i < ssz; i++)
+		printf(" ");
+
+	printf("%s\n", buf);
+	fflush(stdout);
+
+	free(buf);
+	free(os);
+}
+
+
+static void
+header(struct termp *p, const struct mdoc_meta *meta)
+{
+	char		*buf, *title;
+	const char	*pp, *msec;
+	size_t		 ssz, tsz, ttsz, i;;
+
+	if (NULL == (buf = malloc(p->rmargin)))
+		err(1, "malloc");
+	if (NULL == (title = malloc(p->rmargin)))
+		err(1, "malloc");
+
+	if (NULL == (pp = mdoc_vol2a(meta->vol)))
+		switch (meta->msec) {
+		case (MSEC_1):
+			/* FALLTHROUGH */
+		case (MSEC_6):
+			/* FALLTHROUGH */
+		case (MSEC_7):
+			pp = mdoc_vol2a(VOL_URM);
+			break;
+		case (MSEC_8):
+			pp = mdoc_vol2a(VOL_SMM);
+			break;
+		case (MSEC_2):
+			/* FALLTHROUGH */
+		case (MSEC_3):
+			/* FALLTHROUGH */
+		case (MSEC_4):
+			/* FALLTHROUGH */
+		case (MSEC_5):
+			pp = mdoc_vol2a(VOL_PRM);
+			break;
+		case (MSEC_9):
+			pp = mdoc_vol2a(VOL_KM);
+			break;
+		default:
+			/* FIXME: capitalise. */
+			if (NULL == (pp = mdoc_msec2a(meta->msec)))
+				pp = mdoc_msec2a(MSEC_local);
+			break;
+		}
+	assert(pp);
+
+	tsz = strlcpy(buf, pp, p->rmargin);
+	assert(tsz < p->rmargin);
+
+	if ((pp = mdoc_arch2a(meta->arch))) {
+		tsz = strlcat(buf, " (", p->rmargin);
+		assert(tsz < p->rmargin);
+		tsz = strlcat(buf, pp, p->rmargin);
+		assert(tsz < p->rmargin);
+		tsz = strlcat(buf, ")", p->rmargin);
+		assert(tsz < p->rmargin);
+	}
+
+	ttsz = strlcpy(title, meta->title, p->rmargin);
+
+	if (NULL == (msec = mdoc_msec2a(meta->msec)))
+		msec = "";
+
+	ssz = (2 * (ttsz + 2 + strlen(msec))) + tsz + 2;
+
+	if (ssz > p->rmargin) {
+		if ((ssz -= p->rmargin) % 2)
+			ssz++;
+		ssz /= 2;
+	
+		assert(ssz <= ttsz);
+		title[ttsz - ssz] = 0;
+		ssz = 1;
+	} else
+		ssz = ((p->rmargin - ssz) / 2) + 1;
+
+	printf("%s(%s)", title, msec);
+
+	for (i = 0; i < ssz; i++)
+		printf(" ");
+
+	printf("%s", buf);
+
+	for (i = 0; i < ssz; i++)
+		printf(" ");
+
+	printf("%s(%s)\n", title, msec);
+	fflush(stdout);
+
+	free(title);
+	free(buf);
+}
diff --git a/mdoctree.c b/mdoctree.c
index 2c62359c..444dde48 100644
--- a/mdoctree.c
+++ b/mdoctree.c
@@ -1,4 +1,4 @@
-	/* $Id: mdoctree.c,v 1.2 2009/02/22 22:58:39 kristaps Exp $ */
+	/* $Id: mdoctree.c,v 1.3 2009/02/23 07:09:13 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -19,55 +19,130 @@
 #include <assert.h>
 #include <err.h>
 #include <getopt.h>
+#include <stdio.h>
 #include <stdlib.h>
 
 #include "mmain.h"
 
-extern	void	treeprint(const struct mdoc_node *,
-			const struct mdoc_meta *);
+#define	xprintf (void)printf
+
+static	void	doprint(const struct mdoc_node *, int);
 
 int
 main(int argc, char *argv[])
 {
 	struct mmain	*p;
 	const struct mdoc *mdoc;
-	int		 c;
 
 	extern int	 optreset;
 	extern int	 optind;
 
 	p = mmain_alloc();
 
-	if ( ! mmain_getopt(p, argc, argv, NULL))
+	if ( ! mmain_getopt(p, argc, argv, NULL, NULL, NULL, NULL))
 		mmain_exit(p, 1);
 
-	optreset = optind = 1;
-	printf("here\n");
-
-	while (-1 != (c = getopt(argc, argv, "f:")))
-		switch (c) {
-		case ('f'):
-			printf("%s\n", optarg);
-			break;
-		case ('?'):
-			if (mmain_isopt(optopt)) {
-				printf("ok: %d\n", optopt);
-				break;
-			}
-			printf("bad: %d\n", optopt);
-			/* FALLTHROUGH */
-		default:
-			mmain_usage(NULL);
-			mmain_exit(p, 1);
-			/* NOTREACHED */
-		}
-
 	if (NULL == (mdoc = mmain_mdoc(p)))
 		mmain_exit(p, 1);
 
-	treeprint(mdoc_node(mdoc), mdoc_meta(mdoc));
+	doprint(mdoc_node(mdoc), 0);
 	mmain_exit(p, 0);
 	/* NOTREACHED */
 }
 
 
+static void
+doprint(const struct mdoc_node *n, int indent)
+{
+	const char	 *p, *t;
+	int		  i, j;
+	size_t		  argc, sz;
+	char		**params;
+	struct mdoc_arg	 *argv;
+
+	argv = NULL;
+	argc = sz = 0;
+	params = NULL;
+
+	switch (n->type) {
+	case (MDOC_ROOT):
+		t = "root";
+		break;
+	case (MDOC_BLOCK):
+		t = "block";
+		break;
+	case (MDOC_HEAD):
+		t = "block-head";
+		break;
+	case (MDOC_BODY):
+		t = "block-body";
+		break;
+	case (MDOC_TAIL):
+		t = "block-tail";
+		break;
+	case (MDOC_ELEM):
+		t = "elem";
+		break;
+	case (MDOC_TEXT):
+		t = "text";
+		break;
+	default:
+		abort();
+		/* NOTREACHED */
+	}
+
+	switch (n->type) {
+	case (MDOC_TEXT):
+		p = n->data.text.string;
+		break;
+	case (MDOC_BODY):
+		p = mdoc_macronames[n->tok];
+		break;
+	case (MDOC_HEAD):
+		p = mdoc_macronames[n->tok];
+		break;
+	case (MDOC_TAIL):
+		p = mdoc_macronames[n->tok];
+		break;
+	case (MDOC_ELEM):
+		p = mdoc_macronames[n->tok];
+		argv = n->data.elem.argv;
+		argc = n->data.elem.argc;
+		break;
+	case (MDOC_BLOCK):
+		p = mdoc_macronames[n->tok];
+		argv = n->data.block.argv;
+		argc = n->data.block.argc;
+		break;
+	case (MDOC_ROOT):
+		p = "root";
+		break;
+	default:
+		abort();
+		/* NOTREACHED */
+	}
+
+	for (i = 0; i < indent; i++)
+		xprintf("    ");
+	xprintf("%s (%s)", p, t);
+
+	for (i = 0; i < (int)argc; i++) {
+		xprintf(" -%s", mdoc_argnames[argv[i].arg]);
+		if (argv[i].sz > 0)
+			xprintf(" [");
+		for (j = 0; j < (int)argv[i].sz; j++)
+			xprintf(" [%s]", argv[i].value[j]);
+		if (argv[i].sz > 0)
+			xprintf(" ]");
+	}
+
+	for (i = 0; i < (int)sz; i++)
+		xprintf(" [%s]", params[i]);
+
+	xprintf(" %d:%d\n", n->line, n->pos);
+
+	if (n->child)
+		doprint(n->child, indent + 1);
+	if (n->next)
+		doprint(n->next, indent);
+}
diff --git a/mmain.c b/mmain.c
index 5e41683a..370d23f8 100644
--- a/mmain.c
+++ b/mmain.c
@@ -1,4 +1,4 @@
-	/* $Id: mmain.c,v 1.1 2009/02/22 22:58:39 kristaps Exp $ */
+	/* $Id: mmain.c,v 1.2 2009/02/23 07:09:13 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -42,7 +42,7 @@ struct	mmain {
 	struct mdoc	 *mdoc;		/* Active parser. */
 	char		 *buf;		/* Input buffer. */
 	u_long		  bufsz;	/* Input buffer size. */
-	char		  in[MAXPATHLEN]; /* Input file name. */
+	char		 *in; 		/* Input file name. */
 	int		  fdin;		/* Input file desc. */
 };
 
@@ -60,6 +60,9 @@ extern	int		  getsubopt(char **, char *const *, char **);
 #endif
 
 
+/*
+ * Print our and our caller's usage message.
+ */
 void
 mmain_usage(const char *help)
 {
@@ -69,6 +72,9 @@ mmain_usage(const char *help)
 }
 
 
+/*
+ * Allocate the convenience library and initialise some values.
+ */ 
 struct mmain *
 mmain_alloc(void)
 {
@@ -77,36 +83,41 @@ mmain_alloc(void)
 	if (NULL == (p = calloc(1, sizeof(struct mmain))))
 		err(1, "malloc");
 
-	(void)strlcpy(p->in, "-", MAXPATHLEN);
+	p->in = "-";
 	p->fdin = STDIN_FILENO;
 
 	return(p);
 }
 
 
+/*
+ * Parse command-line options.  Accepts a small (<28 char) opstring "u"
+ * parameter (e.g. "ho:") or NULL, a corresponding "help" string (e.g.
+ * "[-h] [-o output]" or NULL, a callback function for parsed arguments
+ * and an opaque pointer argument for that function.
+ */
 int
-mmain_isopt(int c)
+mmain_getopt(struct mmain *p, int argc, char *argv[], 
+		const char *help, const char *u, void *arg,
+		int (*getopt_cb)(void *, int, const char *))
 {
-	
-	switch (c) {
-	case ('v'):
-		/* FALLTHROUGH */
-	case ('W'):
-		return(1);
-	default:
-		break;
-	}
-	return(0);
-}
+	int		 c;
+	char		 opts[32]; /* XXX */
+	size_t		 sz;
 
+	extern int	 optind;
+	extern int	 optreset;
 
-int
-mmain_getopt(struct mmain *p, int argc,
-		char *argv[], const char *help)
-{
-	int		 c;
+	sz = strlcpy(opts, "vW:", 32);
+	assert(sz < 32);
+
+	if (u) {
+		sz = strlcat(opts, u, 32);
+		assert(sz < 32);
+	}
 
-	while (-1 != (c = getopt(argc, argv, ":vW:")))
+	/* LINTED */
+	while (-1 != (c = getopt(argc, argv, opts)))
 		switch (c) {
 		case ('v'):
 			p->dbg++;
@@ -116,23 +127,20 @@ mmain_getopt(struct mmain *p, int argc,
 				return(0);
 			break;
 		case ('?'):
-			break;
-		default:
 			mmain_usage(help);
 			return(0);
+		default:
+			assert(getopt_cb);
+			if ((*getopt_cb)(arg, c, optarg))
+				break;
+			return(0);
 		}
 
 	argv += optind;
-	argc -= optind;
+	if ((argc -= optind) > 0)
+		p->in = *argv++;
 
-	if (0 == argc)
-		return(1);
-
-	if (strlcpy(p->in, *argv++, MAXPATHLEN) < MAXPATHLEN)
-		return(1);
-
-	warnx("filename too long");
-	return(0);
+	return(1);
 }
 
 
diff --git a/mmain.h b/mmain.h
index eb90ff7d..af907531 100644
--- a/mmain.h
+++ b/mmain.h
@@ -1,4 +1,4 @@
-/* $Id: mmain.h,v 1.1 2009/02/22 22:58:39 kristaps Exp $ */
+/* $Id: mmain.h,v 1.2 2009/02/23 07:09:13 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -22,8 +22,8 @@
 /* 
  * This is a convenience library for utilities implementing mdoc(3)
  * accepting a similar set of command-line patterns.  mmain handles
- * error reporting (to the terminal), preparing and reading the input
- * file, and enacting the parse itself.
+ * error reporting (to the terminal), command-line parsing, preparing
+ * and reading the input file, and enacting the parse itself.
  */
 
 #include "mdoc.h"
@@ -34,9 +34,9 @@ struct	mmain;
 
 struct	mmain		*mmain_alloc(void);
 __dead void		 mmain_exit(struct mmain *, int);
-int			 mmain_getopt(struct mmain *, int, 
-				char *[], const char *);
-int			 mmain_isopt(int);
+int			 mmain_getopt(struct mmain *, int, char *[], 
+				const char *, const char *, void *,
+				int (*)(void *, int, const char *));
 struct mdoc		*mmain_mdoc(struct mmain *);
 void			 mmain_usage(const char *);
 
diff --git a/termact.c b/termact.c
deleted file mode 100644
index 0151dfc3..00000000
--- a/termact.c
+++ /dev/null
@@ -1,1042 +0,0 @@
-/* $Id: termact.c,v 1.6 2009/02/22 19:23:48 kristaps Exp $ */
-/*
- * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "term.h"
-
-#define	INDENT		  4
-
-/*
- * Performs actions on nodes of the abstract syntax tree.  Both pre- and
- * post-fix operations are defined here.
- */
-
-#define	TTYPE_PROG	  0
-#define	TTYPE_CMD_FLAG	  1
-#define	TTYPE_CMD_ARG	  2
-#define	TTYPE_SECTION	  3
-#define	TTYPE_FUNC_DECL	  4
-#define	TTYPE_VAR_DECL	  5
-#define	TTYPE_FUNC_TYPE	  6
-#define	TTYPE_FUNC_NAME	  7
-#define	TTYPE_FUNC_ARG	  8
-#define	TTYPE_LINK	  9
-#define	TTYPE_NMAX	  10
-
-/* 
- * These define "styles" for element types, like command arguments or
- * executable names.  This is useful when multiple macros must decorate
- * the same thing (like .Ex -std cmd and .Nm cmd). 
- */
-
-const	int ttypes[TTYPE_NMAX] = {
-	TERMP_BOLD,		/* TTYPE_PROG */
-	TERMP_BOLD,		/* TTYPE_CMD_FLAG */
-	TERMP_UNDERLINE, 	/* TTYPE_CMD_ARG */
-	TERMP_BOLD, 		/* TTYPE_SECTION */
-	TERMP_BOLD,		/* TTYPE_FUNC_DECL */
-	TERMP_UNDERLINE,	/* TTYPE_VAR_DECL */
-	TERMP_UNDERLINE,	/* TTYPE_FUNC_TYPE */
-	TERMP_BOLD, 		/* TTYPE_FUNC_NAME */
-	TERMP_UNDERLINE, 	/* TTYPE_FUNC_ARG */
-	TERMP_UNDERLINE 	/* TTYPE_LINK */
-};
-
-static	int		  arg_hasattr(int, size_t, 
-				const struct mdoc_arg *);
-static	int		  arg_getattr(int, size_t, 
-				const struct mdoc_arg *);
-static	size_t		  arg_offset(const char *);
-
-/*
- * What follows describes prefix and postfix operations for the abstract
- * syntax tree descent.
- */
-
-#define	DECL_ARGS \
-	struct termp *p, \
-	const struct mdoc_meta *meta, \
-	const struct mdoc_node *node
-
-#define	DECL_PRE(name) \
-static	int	 	  name##_pre(DECL_ARGS)
-#define	DECL_POST(name) \
-static	void	 	  name##_post(DECL_ARGS)
-
-DECL_PRE(termp_aq);
-DECL_PRE(termp_ar);
-DECL_PRE(termp_bd);
-DECL_PRE(termp_d1);
-DECL_PRE(termp_dq);
-DECL_PRE(termp_ex);
-DECL_PRE(termp_fa);
-DECL_PRE(termp_fd);
-DECL_PRE(termp_fl);
-DECL_PRE(termp_fn);
-DECL_PRE(termp_ft);
-DECL_PRE(termp_it);
-DECL_PRE(termp_nd);
-DECL_PRE(termp_nm);
-DECL_PRE(termp_ns);
-DECL_PRE(termp_nx);
-DECL_PRE(termp_op);
-DECL_PRE(termp_ox);
-DECL_PRE(termp_pf);
-DECL_PRE(termp_pp);
-DECL_PRE(termp_qq);
-DECL_PRE(termp_sh);
-DECL_PRE(termp_sq);
-DECL_PRE(termp_sx);
-DECL_PRE(termp_ud);
-DECL_PRE(termp_va);
-DECL_PRE(termp_vt);
-DECL_PRE(termp_xr);
-
-DECL_POST(termp_aq);
-DECL_POST(termp_ar);
-DECL_POST(termp_bl);
-DECL_POST(termp_d1);
-DECL_POST(termp_dq);
-DECL_POST(termp_fa);
-DECL_POST(termp_fd);
-DECL_POST(termp_fl);
-DECL_POST(termp_fn);
-DECL_POST(termp_ft);
-DECL_POST(termp_it);
-DECL_POST(termp_nm);
-DECL_POST(termp_op);
-DECL_POST(termp_pf);
-DECL_POST(termp_qq);
-DECL_POST(termp_sh);
-DECL_POST(termp_sq);
-DECL_POST(termp_sx);
-DECL_POST(termp_va);
-DECL_POST(termp_vt);
-
-const	struct termact __termacts[MDOC_MAX] = {
-	{ NULL, NULL }, /* \" */
-	{ NULL, NULL }, /* Dd */
-	{ NULL, NULL }, /* Dt */
-	{ NULL, NULL }, /* Os */
-	{ termp_sh_pre, termp_sh_post }, /* Sh */
-	{ NULL, NULL }, /* Ss */ 
-	{ termp_pp_pre, NULL }, /* Pp */ 
-	{ termp_d1_pre, termp_d1_post }, /* D1 */
-	{ NULL, NULL }, /* Dl */
-	{ termp_bd_pre, NULL }, /* Bd */
-	{ NULL, NULL }, /* Ed */
-	{ NULL, termp_bl_post }, /* Bl */
-	{ NULL, NULL }, /* El */
-	{ termp_it_pre, termp_it_post }, /* It */
-	{ NULL, NULL }, /* Ad */ 
-	{ NULL, NULL }, /* An */
-	{ termp_ar_pre, termp_ar_post }, /* Ar */
-	{ NULL, NULL }, /* Cd */
-	{ NULL, NULL }, /* Cm */
-	{ NULL, NULL }, /* Dv */ 
-	{ NULL, NULL }, /* Er */ 
-	{ NULL, NULL }, /* Ev */ 
-	{ termp_ex_pre, NULL }, /* Ex */
-	{ termp_fa_pre, termp_fa_post }, /* Fa */ 
-	{ termp_fd_pre, termp_fd_post }, /* Fd */ 
-	{ termp_fl_pre, termp_fl_post }, /* Fl */
-	{ termp_fn_pre, termp_fn_post }, /* Fn */ 
-	{ termp_ft_pre, termp_ft_post }, /* Ft */ 
-	{ NULL, NULL }, /* Ic */ 
-	{ NULL, NULL }, /* In */ 
-	{ NULL, NULL }, /* Li */
-	{ termp_nd_pre, NULL }, /* Nd */ 
-	{ termp_nm_pre, termp_nm_post }, /* Nm */ 
-	{ termp_op_pre, termp_op_post }, /* Op */
-	{ NULL, NULL }, /* Ot */
-	{ NULL, NULL }, /* Pa */
-	{ NULL, NULL }, /* Rv */
-	{ NULL, NULL }, /* St */ 
-	{ termp_va_pre, termp_va_post }, /* Va */
-	{ termp_vt_pre, termp_vt_post }, /* Vt */ 
-	{ termp_xr_pre, NULL }, /* Xr */
-	{ NULL, NULL }, /* %A */
-	{ NULL, NULL }, /* %B */
-	{ NULL, NULL }, /* %D */
-	{ NULL, NULL }, /* %I */
-	{ NULL, NULL }, /* %J */
-	{ NULL, NULL }, /* %N */
-	{ NULL, NULL }, /* %O */
-	{ NULL, NULL }, /* %P */
-	{ NULL, NULL }, /* %R */
-	{ NULL, NULL }, /* %T */
-	{ NULL, NULL }, /* %V */
-	{ NULL, NULL }, /* Ac */
-	{ NULL, NULL }, /* Ao */
-	{ termp_aq_pre, termp_aq_post }, /* Aq */
-	{ NULL, NULL }, /* At */
-	{ NULL, NULL }, /* Bc */
-	{ NULL, NULL }, /* Bf */ 
-	{ NULL, NULL }, /* Bo */
-	{ NULL, NULL }, /* Bq */
-	{ NULL, NULL }, /* Bsx */
-	{ NULL, NULL }, /* Bx */
-	{ NULL, NULL }, /* Db */
-	{ NULL, NULL }, /* Dc */
-	{ NULL, NULL }, /* Do */
-	{ termp_dq_pre, termp_dq_post }, /* Dq */
-	{ NULL, NULL }, /* Ec */
-	{ NULL, NULL }, /* Ef */
-	{ NULL, NULL }, /* Em */ 
-	{ NULL, NULL }, /* Eo */
-	{ NULL, NULL }, /* Fx */
-	{ NULL, NULL }, /* Ms */
-	{ NULL, NULL }, /* No */
-	{ termp_ns_pre, NULL }, /* Ns */
-	{ termp_nx_pre, NULL }, /* Nx */
-	{ termp_ox_pre, NULL }, /* Ox */
-	{ NULL, NULL }, /* Pc */
-	{ termp_pf_pre, termp_pf_post }, /* Pf */
-	{ NULL, NULL }, /* Po */
-	{ NULL, NULL }, /* Pq */
-	{ NULL, NULL }, /* Qc */
-	{ NULL, NULL }, /* Ql */
-	{ NULL, NULL }, /* Qo */
-	{ termp_qq_pre, termp_qq_post }, /* Qq */
-	{ NULL, NULL }, /* Re */
-	{ NULL, NULL }, /* Rs */
-	{ NULL, NULL }, /* Sc */
-	{ NULL, NULL }, /* So */
-	{ termp_sq_pre, termp_sq_post }, /* Sq */
-	{ NULL, NULL }, /* Sm */
-	{ termp_sx_pre, termp_sx_post }, /* Sx */
-	{ NULL, NULL }, /* Sy */
-	{ NULL, NULL }, /* Tn */
-	{ NULL, NULL }, /* Ux */
-	{ NULL, NULL }, /* Xc */
-	{ NULL, NULL }, /* Xo */
-	{ NULL, NULL }, /* Fo */ 
-	{ NULL, NULL }, /* Fc */ 
-	{ NULL, NULL }, /* Oo */
-	{ NULL, NULL }, /* Oc */
-	{ NULL, NULL }, /* Bk */
-	{ NULL, NULL }, /* Ek */
-	{ NULL, NULL }, /* Bt */
-	{ NULL, NULL }, /* Hf */
-	{ NULL, NULL }, /* Fr */
-	{ termp_ud_pre, NULL }, /* Ud */
-};
-
-const struct termact *termacts = __termacts;
-
-
-static size_t
-arg_offset(const char *v)
-{
-	if (0 == strcmp(v, "indent"))
-		return(INDENT);
-	if (0 == strcmp(v, "indent-two"))
-		return(INDENT * 2);
-
-	/* TODO */
-	return(0);
-}
-
-
-static int
-arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv)
-{
-
-	return(-1 != arg_getattr(arg, argc, argv));
-}
-
-
-static int
-arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv)
-{
-	int		 i;
-
-	for (i = 0; i < (int)argc; i++) 
-		if (argv[i].arg == arg)
-			return(i);
-	return(-1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_dq_pre(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return(1);
-
-	word(p, "``");
-	p->flags |= TERMP_NOSPACE;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_dq_post(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return;
-
-	p->flags |= TERMP_NOSPACE;
-	word(p, "''");
-}
-
-
-/* ARGSUSED */
-static void
-termp_it_post(DECL_ARGS)
-{
-	const struct mdoc_node *n, *it;
-	const struct mdoc_block *bl;
-	int		 i;
-	size_t		 width;
-
-	/*
-	 * This (and termp_it_pre()) are the most complicated functions
-	 * here.  They must account for a considerable number of
-	 * switches that completely change the output behaviour, like
-	 * -tag versus -column.  Yech.
-	 */
-
-	switch (node->type) {
-	case (MDOC_BODY):
-		/* FALLTHROUGH */
-	case (MDOC_HEAD):
-		break;
-	default:
-		return;
-	}
-
-	it = node->parent;
-	assert(MDOC_BLOCK == it->type);
-	assert(MDOC_It == it->tok);
-
-	n = it->parent;
-	assert(MDOC_BODY == n->type);
-	assert(MDOC_Bl == n->tok);
-	n = n->parent;
-	bl = &n->data.block;
-
-	/* If `-tag', adjust our margins accordingly. */
-
-	if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
-		i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
-		assert(i >= 0);
-		assert(1 == bl->argv[i].sz);
-		width = strlen(*bl->argv[i].value); /* XXX */
-
-		if (MDOC_HEAD == node->type) {
-			flushln(p);
-			/* FIXME: nested lists. */
-			p->rmargin = p->maxrmargin;
-			p->flags &= ~TERMP_NOBREAK;
-		} else {
-			flushln(p);
-			p->offset -= width + 1;
-			p->flags &= ~TERMP_NOLPAD;
-		}
-		return;
-	}
-
-	if (arg_hasattr(MDOC_Ohang, bl->argc, bl->argv)) {
-		i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
-		assert(i >= 0);
-		assert(1 == bl->argv[i].sz);
-		width = arg_offset(*bl->argv[i].value);
-
-		flushln(p);
-		p->offset -= width + 1;
-		return;
-	}
-}
-
-
-/* ARGSUSED */
-static int
-termp_it_pre(DECL_ARGS)
-{
-	const struct mdoc_node *n, *it;
-	const struct mdoc_block *bl;
-	int		 i;
-	size_t		 width;
-
-	/*
-	 * Also see termp_it_post() for general comments.
-	 */
-
-	switch (node->type) {
-	case (MDOC_BODY):
-		/* FALLTHROUGH */
-	case (MDOC_HEAD):
-		it = node->parent;
-		break;
-	case (MDOC_BLOCK):
-		it = node;
-		break;
-	default:
-		return(1);
-	}
-
-	assert(MDOC_BLOCK == it->type);
-	assert(MDOC_It == it->tok);
-
-	n = it->parent;
-	assert(MDOC_BODY == n->type);
-	assert(MDOC_Bl == n->tok);
-	n = n->parent;
-	bl = &n->data.block;
-
-	/* If `-compact', don't assert vertical space. */
-
-	if (MDOC_BLOCK == node->type) {
-		if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
-			newln(p);
-		else
-			vspace(p);
-		return(1);
-	}
-
-	assert(MDOC_HEAD == node->type 
-			|| MDOC_BODY == node->type);
-
-	/* If `-tag', adjust our margins accordingly. */
-
-	if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
-		i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
-		assert(i >= 0); /* XXX */
-		assert(1 == bl->argv[i].sz);
-		width = strlen(*bl->argv[i].value); /* XXX */
-
-		/* FIXME: nested lists. */
-
-		if (MDOC_HEAD == node->type) {
-			p->flags |= TERMP_NOBREAK;
-			p->flags |= TERMP_NOSPACE;
-			p->rmargin = p->offset + width;
-		} else {
-			p->flags |= TERMP_NOSPACE;
-			p->flags |= TERMP_NOLPAD;
-			p->offset += width + 1;
-		}
-		return(1);
-	}
-
-	/* If `-ohang', adjust left-margin. */
-
-	if (arg_hasattr(MDOC_Ohang, bl->argc, bl->argv)) {
-		i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
-		assert(i >= 0);
-		assert(1 == bl->argv[i].sz);
-		width = arg_offset(*bl->argv[i].value);
-
-		p->flags |= TERMP_NOSPACE;
-		p->offset += width + 1;
-		return(1);
-	}
-
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_nm_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_PROG];
-}
-
-
-/* ARGSUSED */
-static void
-termp_fl_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_CMD_FLAG];
-}
-
-
-/* ARGSUSED */
-static int
-termp_ar_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_CMD_ARG];
-	if (NULL == node->child)
-		word(p, "...");
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_nm_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_PROG];
-	if (NULL == node->child)
-		word(p, meta->name);
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_ns_pre(DECL_ARGS)
-{
-
-	p->flags |= TERMP_NOSPACE;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_pp_pre(DECL_ARGS)
-{
-
-	vspace(p);
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_ar_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_CMD_ARG];
-}
-
-
-/* ARGSUSED */
-static int
-termp_ex_pre(DECL_ARGS)
-{
-	int		 i;
-
-	i = arg_getattr(MDOC_Std, node->data.elem.argc, 
-			node->data.elem.argv);
-	assert(i >= 0);
-
-	word(p, "The");
-	p->flags |= ttypes[TTYPE_PROG];
-	word(p, *node->data.elem.argv[i].value);
-	p->flags &= ~ttypes[TTYPE_PROG];
-       	word(p, "utility exits 0 on success, and >0 if an error occurs.");
-
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_nd_pre(DECL_ARGS)
-{
-
-	word(p, "\\-");
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_bl_post(DECL_ARGS)
-{
-
-	if (MDOC_BLOCK == node->type)
-		newln(p);
-}
-
-
-/* ARGSUSED */
-static void
-termp_op_post(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type) 
-		return;
-	p->flags |= TERMP_NOSPACE;
-	word(p, "\\(rB");
-}
-
-
-/* ARGSUSED */
-static void
-termp_sh_post(DECL_ARGS)
-{
-
-	switch (node->type) {
-	case (MDOC_HEAD):
-		p->flags &= ~ttypes[TTYPE_SECTION];
-		newln(p);
-		break;
-	case (MDOC_BODY):
-		newln(p);
-		p->offset -= INDENT;
-		break;
-	default:
-		break;
-	}
-}
-
-
-/* ARGSUSED */
-static int
-termp_xr_pre(DECL_ARGS)
-{
-	const struct mdoc_node *n;
-
-	n = node->child;
-	assert(n);
-
-	assert(MDOC_TEXT == n->type);
-	word(p, n->data.text.string);
-
-	if (NULL == (n = n->next)) 
-		return(0);
-
-	assert(MDOC_TEXT == n->type);
-	p->flags |= TERMP_NOSPACE;
-	word(p, "(");
-	p->flags |= TERMP_NOSPACE;
-	word(p, n->data.text.string);
-	p->flags |= TERMP_NOSPACE;
-	word(p, ")");
-
-	return(0);
-}
-
-
-/* ARGSUSED */
-static int
-termp_vt_pre(DECL_ARGS)
-{
-
-	/* FIXME: this can be "type name". */
-	p->flags |= ttypes[TTYPE_VAR_DECL];
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_vt_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_VAR_DECL];
-	if (node->sec == SEC_SYNOPSIS)
-		vspace(p);
-}
-
-
-/* ARGSUSED */
-static int
-termp_fd_pre(DECL_ARGS)
-{
-
-	/* 
-	 * FIXME: this naming is bad.  This value is used, in general,
-	 * for the #include header or other preprocessor statement.
-	 */
-	p->flags |= ttypes[TTYPE_FUNC_DECL];
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_fd_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_FUNC_DECL];
-	if (node->sec == SEC_SYNOPSIS)
-		vspace(p);
-
-}
-
-
-/* ARGSUSED */
-static int
-termp_sh_pre(DECL_ARGS)
-{
-
-	switch (node->type) {
-	case (MDOC_HEAD):
-		vspace(p);
-		p->flags |= ttypes[TTYPE_SECTION];
-		break;
-	case (MDOC_BODY):
-		p->offset += INDENT;
-		break;
-	default:
-		break;
-	}
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_op_pre(DECL_ARGS)
-{
-
-	switch (node->type) {
-	case (MDOC_BODY):
-		word(p, "\\(lB");
-		p->flags |= TERMP_NOSPACE;
-		break;
-	default:
-		break;
-	}
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_ud_pre(DECL_ARGS)
-{
-
-	word(p, "currently under development.");
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_fl_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_CMD_FLAG];
-	word(p, "\\-");
-	p->flags |= TERMP_NOSPACE;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_d1_pre(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return(1);
-	newln(p);
-	p->offset += INDENT;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_d1_post(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type) 
-		return;
-	newln(p);
-	p->offset -= INDENT;
-}
-
-
-/* ARGSUSED */
-static int
-termp_aq_pre(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return(1);
-	word(p, "<");
-	p->flags |= TERMP_NOSPACE;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_aq_post(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return;
-	p->flags |= TERMP_NOSPACE;
-	word(p, ">");
-}
-
-
-/* ARGSUSED */
-static int
-termp_ft_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_FUNC_TYPE];
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_ft_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_FUNC_TYPE];
-	if (node->sec == SEC_SYNOPSIS)
-		newln(p);
-
-}
-
-
-/* ARGSUSED */
-static int
-termp_fn_pre(DECL_ARGS)
-{
-	const struct mdoc_node *n;
-
-	assert(node->child);
-	assert(MDOC_TEXT == node->child->type);
-
-	/* FIXME: can be "type funcname" "type varname"... */
-
-	p->flags |= ttypes[TTYPE_FUNC_NAME];
-	word(p, node->child->data.text.string);
-	p->flags &= ~ttypes[TTYPE_FUNC_NAME];
-
-	p->flags |= TERMP_NOSPACE;
-	word(p, "(");
-
-	p->flags |= TERMP_NOSPACE;
-	for (n = node->child->next; n; n = n->next) {
-		assert(MDOC_TEXT == n->type);
-		p->flags |= ttypes[TTYPE_FUNC_ARG];
-		word(p, n->data.text.string);
-		p->flags &= ~ttypes[TTYPE_FUNC_ARG];
-		if ((n->next))
-			word(p, ",");
-	}
-
-	p->flags |= TERMP_NOSPACE;
-	word(p, ")");
-
-	if (SEC_SYNOPSIS == node->sec)
-		word(p, ";");
-
-	return(0);
-}
-
-
-/* ARGSUSED */
-static void
-termp_fn_post(DECL_ARGS)
-{
-
-	if (node->sec == SEC_SYNOPSIS)
-		vspace(p);
-
-}
-
-
-/* ARGSUSED */
-static int
-termp_sx_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_LINK];
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_sx_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_LINK];
-}
-
-
-/* ARGSUSED */
-static int
-termp_fa_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_FUNC_ARG];
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_fa_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_FUNC_ARG];
-}
-
-
-/* ARGSUSED */
-static int
-termp_va_pre(DECL_ARGS)
-{
-
-	p->flags |= ttypes[TTYPE_VAR_DECL];
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_va_post(DECL_ARGS)
-{
-
-	p->flags &= ~ttypes[TTYPE_VAR_DECL];
-}
-
-
-/* ARGSUSED */
-static int
-termp_bd_pre(DECL_ARGS)
-{
-	const struct mdoc_block *bl;
-	const struct mdoc_node *n;
-
-	if (MDOC_BLOCK == node->type) {
-		vspace(p);
-		return(1);
-	} else if (MDOC_BODY != node->type)
-		return(1);
-
-	assert(MDOC_BLOCK == node->parent->type);
-
-	bl = &node->parent->data.block;
-	if ( ! arg_hasattr(MDOC_Literal, bl->argc, bl->argv))
-		return(1);
-
-	p->flags |= TERMP_LITERAL;
-
-	for (n = node->child; n; n = n->next) {
-		assert(MDOC_TEXT == n->type); /* FIXME */
-		if ((*n->data.text.string)) {
-			word(p, n->data.text.string);
-			flushln(p);
-		} else
-			vspace(p);
-
-	}
-
-	p->flags &= ~TERMP_LITERAL;
-	return(0);
-}
-
-
-/* ARGSUSED */
-static int
-termp_qq_pre(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return(1);
-	word(p, "\"");
-	p->flags |= TERMP_NOSPACE;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_qq_post(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return;
-	p->flags |= TERMP_NOSPACE;
-	word(p, "\"");
-}
-
-
-/* ARGSUSED */
-static int
-termp_ox_pre(DECL_ARGS)
-{
-
-	word(p, "OpenBSD");
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_nx_pre(DECL_ARGS)
-{
-
-	word(p, "NetBSD");
-	return(1);
-}
-
-
-/* ARGSUSED */
-static int
-termp_sq_pre(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return(1);
-	word(p, "`");
-	p->flags |= TERMP_NOSPACE;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_sq_post(DECL_ARGS)
-{
-
-	if (MDOC_BODY != node->type)
-		return;
-	p->flags |= TERMP_NOSPACE;
-	word(p, "\'");
-}
-
-
-/* ARGSUSED */
-static int
-termp_pf_pre(DECL_ARGS)
-{
-
-	p->flags |= TERMP_IGNDELIM;
-	return(1);
-}
-
-
-/* ARGSUSED */
-static void
-termp_pf_post(DECL_ARGS)
-{
-
-	p->flags &= ~TERMP_IGNDELIM;
-	p->flags |= TERMP_NOSPACE;
-}
-
-
diff --git a/tree.c b/tree.c
deleted file mode 100644
index b62c2adf..00000000
--- a/tree.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/* $Id: tree.c,v 1.6 2009/02/21 21:00:06 kristaps Exp $ */
-/*
- * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "mdoc.h"
-
-#define	xprintf		(void)printf
-
-static	void	 treeprint_r(const struct mdoc_node *, int);
-
-static void
-treeprint_r(const struct mdoc_node *n, int indent)
-{
-	const char	 *p, *t;
-	int		  i, j;
-	size_t		  argc, sz;
-	char		**params;
-	struct mdoc_arg	 *argv;
-
-	argv = NULL;
-	argc = sz = 0;
-	params = NULL;
-
-	switch (n->type) {
-	case (MDOC_ROOT):
-		t = "root";
-		break;
-	case (MDOC_BLOCK):
-		t = "block";
-		break;
-	case (MDOC_HEAD):
-		t = "block-head";
-		break;
-	case (MDOC_BODY):
-		t = "block-body";
-		break;
-	case (MDOC_TAIL):
-		t = "block-tail";
-		break;
-	case (MDOC_ELEM):
-		t = "elem";
-		break;
-	case (MDOC_TEXT):
-		t = "text";
-		break;
-	default:
-		abort();
-		/* NOTREACHED */
-	}
-
-	switch (n->type) {
-	case (MDOC_TEXT):
-		p = n->data.text.string;
-		break;
-	case (MDOC_BODY):
-		p = mdoc_macronames[n->tok];
-		break;
-	case (MDOC_HEAD):
-		p = mdoc_macronames[n->tok];
-		break;
-	case (MDOC_TAIL):
-		p = mdoc_macronames[n->tok];
-		break;
-	case (MDOC_ELEM):
-		p = mdoc_macronames[n->tok];
-		argv = n->data.elem.argv;
-		argc = n->data.elem.argc;
-		break;
-	case (MDOC_BLOCK):
-		p = mdoc_macronames[n->tok];
-		argv = n->data.block.argv;
-		argc = n->data.block.argc;
-		break;
-	case (MDOC_ROOT):
-		p = "root";
-		break;
-	default:
-		abort();
-		/* NOTREACHED */
-	}
-
-	for (i = 0; i < indent; i++)
-		xprintf("    ");
-	xprintf("%s (%s)", p, t);
-
-	for (i = 0; i < (int)argc; i++) {
-		xprintf(" -%s", mdoc_argnames[argv[i].arg]);
-		if (argv[i].sz > 0)
-			xprintf(" [");
-		for (j = 0; j < (int)argv[i].sz; j++)
-			xprintf(" [%s]", argv[i].value[j]);
-		if (argv[i].sz > 0)
-			xprintf(" ]");
-	}
-
-	for (i = 0; i < (int)sz; i++)
-		xprintf(" [%s]", params[i]);
-
-	xprintf(" %d:%d\n", n->line, n->pos);
-
-	if (n->child)
-		treeprint_r(n->child, indent + 1);
-	if (n->next)
-		treeprint_r(n->next, indent);
-}
-
-
-/* ARGSUSED */
-void
-treeprint(const struct mdoc_node *node,
-		const struct mdoc_meta *meta)
-{
-
-	treeprint_r(node, 0);
-}