Module Name:    src
Committed By:   christos
Date:           Wed Mar 30 21:30:21 UTC 2016

Added Files:
        src/external/public-domain/sqlite/sqlite2mdoc: LICENSE.md Makefile
            README.md main.c sqlite2mdoc.1

Log Message:
From: https://github.com/kristapsdz/sqlite2mdoc


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 \
    src/external/public-domain/sqlite/sqlite2mdoc/LICENSE.md \
    src/external/public-domain/sqlite/sqlite2mdoc/Makefile \
    src/external/public-domain/sqlite/sqlite2mdoc/README.md \
    src/external/public-domain/sqlite/sqlite2mdoc/main.c \
    src/external/public-domain/sqlite/sqlite2mdoc/sqlite2mdoc.1

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Added files:

Index: src/external/public-domain/sqlite/sqlite2mdoc/LICENSE.md
diff -u /dev/null src/external/public-domain/sqlite/sqlite2mdoc/LICENSE.md:1.1
--- /dev/null	Wed Mar 30 17:30:21 2016
+++ src/external/public-domain/sqlite/sqlite2mdoc/LICENSE.md	Wed Mar 30 17:30:20 2016
@@ -0,0 +1,14 @@
+Copyright (c) 2012--2016, Kristaps Dzonsons <[email protected]>
+
+Permission to use, copy, modify, and/or 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.
+
Index: src/external/public-domain/sqlite/sqlite2mdoc/Makefile
diff -u /dev/null src/external/public-domain/sqlite/sqlite2mdoc/Makefile:1.1
--- /dev/null	Wed Mar 30 17:30:21 2016
+++ src/external/public-domain/sqlite/sqlite2mdoc/Makefile	Wed Mar 30 17:30:20 2016
@@ -0,0 +1,22 @@
+# If you're on Linux, un-comment the following.
+#LDADD	 = -lbsd
+
+#####################################################################
+# You probably don't want to change anything beneath here.
+#####################################################################
+
+CFLAGS	+= -g -W -Wall
+PREFIX	 = /usr/local
+
+sqlite2mdoc: main.o
+	$(CC) -o $@ main.o $(LDADD)
+
+install:
+	mkdir -p $(DESTDIR)$(PREFIX)/bin
+	mkdir -p $(DESTDIR)$(PREFIX)/man/man1
+	install -m 0755 sqlite2mdoc $(DESTDIR)$(PREFIX)/bin
+	install -m 0444 sqlite2mdoc.1 $(DESTDIR)$(PREFIX)/man/man1
+
+clean:
+	rm -f sqlite2mdoc main.o
+	rm -rf sqlite2mdoc.dSYM
Index: src/external/public-domain/sqlite/sqlite2mdoc/README.md
diff -u /dev/null src/external/public-domain/sqlite/sqlite2mdoc/README.md:1.1
--- /dev/null	Wed Mar 30 17:30:21 2016
+++ src/external/public-domain/sqlite/sqlite2mdoc/README.md	Wed Mar 30 17:30:20 2016
@@ -0,0 +1,35 @@
+## Synopsis
+
+This utility accepts an [SQLite](https://www.sqlite.org) header file
+`sqlite.h` and produces a set of decently well-formed
+[mdoc(7)](http://man.openbsd.org/OpenBSD-current/man7/mdoc.7) files
+documenting the C API.
+These will be roughly equivalent to the [C-language Interface
+Specification for SQLite](https://www.sqlite.org/c3ref/intro.html).
+
+You can also use it for any file(s) using the documentation standards of
+SQLite.
+See the [sqlite2mdoc.1](sqlite2mdoc.1) manpage for syntax details.
+
+This [GitHub](https://www.github.com) repository is a read-only mirror
+of the project's CVS repository.
+
+## Installation
+
+Simply run `make`: this utility isn't meant for installation, but for
+integration into your SQLite deployment phase.
+You can run `make install`, however, if you plan on using it for other
+documentation.
+There are no compile-time or run-time dependencies unless you're on
+Linux, in which case you'll need
+[libbsd](https://libbsd.freedesktop.org).
+You'll also need to uncomment the `LDADD` line in the
+[Makefile](Makefile), in this case.
+
+
+This software has been used on OpenBSD, Mac OS X, and Linux machines.
+
+## License
+
+All sources use the ISC (like OpenBSD) license.
+See the [LICENSE.md](LICENSE.md) file for details.
Index: src/external/public-domain/sqlite/sqlite2mdoc/main.c
diff -u /dev/null src/external/public-domain/sqlite/sqlite2mdoc/main.c:1.1
--- /dev/null	Wed Mar 30 17:30:21 2016
+++ src/external/public-domain/sqlite/sqlite2mdoc/main.c	Wed Mar 30 17:30:20 2016
@@ -0,0 +1,1574 @@
+/*	$Id: main.c,v 1.1 2016/03/30 21:30:20 christos Exp $ */
+/*
+ * Copyright (c) 2016 Kristaps Dzonsons <[email protected]>
+ *
+ * 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 AUTHORS DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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.
+ */
+#ifdef __linux__
+#define _GNU_SOURCE
+#endif
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <getopt.h>
+#include <search.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef __linux__
+#include <bsd/stdio.h>
+#include <bsd/stdlib.h>
+#include <bsd/string.h>
+#endif
+
+/*
+ * Phase of parsing input file.
+ */
+enum	phase {
+	PHASE_INIT = 0, /* waiting to encounter definition */
+	PHASE_KEYS, /* have definition, now keywords */
+	PHASE_DESC, /* have keywords, now description */
+	PHASE_SEEALSO,
+	PHASE_DECL /* have description, now declarations */
+};
+
+/*
+ * What kind of declaration (preliminary analysis). 
+ */
+enum	decltype {
+	DECLTYPE_CPP, /* pre-processor */
+	DECLTYPE_C, /* semicolon-closed non-preprocessor */
+	DECLTYPE_NEITHER /* non-preprocessor, no semicolon */
+};
+
+/*
+ * In variables and function declarations, we toss these.
+ */
+enum	preproc {
+	PREPROC_SQLITE_API,
+	PREPROC_SQLITE_DEPRECATED,
+	PREPROC_SQLITE_EXPERIMENTAL,
+	PREPROC_SQLITE_EXTERN,
+	PREPROC__MAX
+};
+
+/*
+ * HTML tags that we recognise.
+ */
+enum	tag {
+	TAG_B_CLOSE,
+	TAG_B_OPEN,
+	TAG_BLOCK_CLOSE,
+	TAG_BLOCK_OPEN,
+	TAG_DD_CLOSE,
+	TAG_DD_OPEN,
+	TAG_DL_CLOSE,
+	TAG_DL_OPEN,
+	TAG_DT_CLOSE,
+	TAG_DT_OPEN,
+	TAG_H3_CLOSE,
+	TAG_H3_OPEN,
+	TAG_LI_CLOSE,
+	TAG_LI_OPEN,
+	TAG_OL_CLOSE,
+	TAG_OL_OPEN,
+	TAG_PRE_CLOSE,
+	TAG_PRE_OPEN,
+	TAG_UL_CLOSE,
+	TAG_UL_OPEN,
+	TAG__MAX
+};
+
+TAILQ_HEAD(defnq, defn);
+TAILQ_HEAD(declq, decl);
+
+/*
+ * A declaration of type DECLTYPE_CPP or DECLTYPE_C.
+ * These need not be unique (if ifdef'd).
+ */
+struct	decl {
+	enum decltype	 type; /* type of declaration */
+	char		*text; /* text */
+	size_t		 textsz; /* strlen(text) */
+	TAILQ_ENTRY(decl) entries;
+};
+
+/*
+ * A definition is basically the manpage contents.
+ */
+struct	defn {
+	char		 *name; /* really Nd */
+	TAILQ_ENTRY(defn) entries;
+	char		 *desc; /* long description */
+	size_t		  descsz; /* strlen(desc) */
+	struct declq	  dcqhead; /* declarations */
+	int		  multiline; /* used when parsing */
+	int		  instruct; /* used when parsing */
+	const char	 *fn; /* parsed from file */
+	size_t		  ln; /* parsed at line */
+	int		  postprocessed; /* good for emission? */
+	char		 *dt; /* manpage title */
+	char		**nms; /* manpage names */
+	size_t		  nmsz; /* number of names */
+	char		 *fname; /* manpage filename */
+	char		 *keybuf; /* raw keywords */
+	size_t		  keybufsz; /* length of "keysbuf" */
+	char		 *seealso; /* see also tags */
+	size_t		  seealsosz; /* length of seealso */
+	char		**xrs; /* parsed "see also" references */
+	size_t		  xrsz; /* number of references */
+	char		**keys; /* parsed keywords */
+	size_t		  keysz; /* number of keywords */
+};
+
+/*
+ * Entire parse routine.
+ */
+struct	parse {
+	enum phase	 phase; /* phase of parse */
+	size_t		 ln; /* line number */
+	const char	*fn; /* open file */
+	struct defnq	 dqhead; /* definitions */
+};
+
+/*
+ * How to handle HTML tags we find in the text.
+ */
+struct	taginfo {
+	const char	*html; /* HTML to key on */
+	const char	*mdoc; /* generate mdoc(7) */
+	unsigned int	 flags;
+#define	TAGINFO_NOBR	 0x01 /* follow w/space, not newline */
+#define	TAGINFO_NOOP	 0x02 /* just strip out */
+#define	TAGINFO_NOSP	 0x04 /* follow w/o space or newline */
+#define	TAGINFO_INLINE	 0x08 /* inline block (notused) */
+};
+
+static	const struct taginfo tags[TAG__MAX] = {
+	{ "</b>", "\\fP", TAGINFO_INLINE }, /* TAG_B_CLOSE */
+	{ "<b>", "\\fB", TAGINFO_INLINE }, /* TAG_B_OPEN */
+	{ "</blockquote>", ".Ed\n.Pp", 0 }, /* TAG_BLOCK_CLOSE */
+	{ "<blockquote>", ".Bd -ragged", 0 }, /* TAG_BLOCK_OPEN */
+	{ "</dd>", "", TAGINFO_NOOP }, /* TAG_DD_CLOSE */
+	{ "<dd>", "", TAGINFO_NOOP }, /* TAG_DD_OPEN */
+	{ "</dl>", ".El\n.Pp", 0 }, /* TAG_DL_CLOSE */
+	{ "<dl>", ".Bl -tag -width Ds", 0 }, /* TAG_DL_OPEN */
+	{ "</dt>", "", TAGINFO_NOBR | TAGINFO_NOSP}, /* TAG_DT_CLOSE */
+	{ "<dt>", ".It", TAGINFO_NOBR }, /* TAG_DT_OPEN */
+	{ "</h3>", "", TAGINFO_NOBR | TAGINFO_NOSP}, /* TAG_H3_CLOSE */
+	{ "<h3>", ".Ss", TAGINFO_NOBR }, /* TAG_H3_OPEN */
+	{ "</li>", "", TAGINFO_NOOP }, /* TAG_LI_CLOSE */
+	{ "<li>", ".It", 0 }, /* TAG_LI_OPEN */
+	{ "</ol>", ".El\n.Pp", 0 }, /* TAG_OL_CLOSE */
+	{ "<ol>", ".Bl -enum", 0 }, /* TAG_OL_OPEN */
+	{ "</pre>", ".Ed\n.Pp", 0 }, /* TAG_PRE_CLOSE */
+	{ "<pre>", ".Bd -literal", 0 }, /* TAG_PRE_OPEN */
+	{ "</ul>", ".El\n.Pp", 0 }, /* TAG_UL_CLOSE */
+	{ "<ul>", ".Bl -bullet", 0 }, /* TAG_UL_OPEN */
+};
+
+static	const char *const preprocs[TAG__MAX] = {
+	"SQLITE_API", /* PREPROC_SQLITE_API */
+	"SQLITE_DEPRECATED", /* PREPROC_SQLITE_DEPRECATED */
+	"SQLITE_EXPERIMENTAL", /* PREPROC_SQLITE_EXPERIMENTAL */
+	"SQLITE_EXTERN", /* PREPROC_SQLITE_EXTERN */
+};
+
+/* Verbose reporting. */
+static	int verbose;
+/* Don't output any files: use stdout. */
+static	int nofile;
+
+static void
+decl_function_add(struct parse *p, char **etext, 
+	size_t *etextsz, const char *cp, size_t len)
+{
+
+	if (' ' != (*etext)[*etextsz - 1]) {
+		*etext = realloc(*etext, *etextsz + 2);
+		if (NULL == *etext)
+			err(EXIT_FAILURE, "%s:%zu: "
+				"realloc", p->fn, p->ln);
+		(*etextsz)++;
+		strlcat(*etext, " ", *etextsz + 1);
+	}
+	*etext = realloc(*etext, *etextsz + len + 1);
+	if (NULL == *etext)
+		err(EXIT_FAILURE, "%s:%zu: realloc", p->fn, p->ln);
+	memcpy(*etext + *etextsz, cp, len);
+	*etextsz += len;
+	(*etext)[*etextsz] = '\0';
+}
+
+static void
+decl_function_copy(struct parse *p, char **etext,
+	size_t *etextsz, const char *cp, size_t len)
+{
+
+	*etext = malloc(len + 1);
+	if (NULL == *etext)
+		err(EXIT_FAILURE, "%s:%zu: strdup", p->fn, p->ln);
+	memcpy(*etext, cp, len);
+	*etextsz = len;
+	(*etext)[*etextsz] = '\0';
+}
+
+/*
+ * A C function (or variable, or whatever).
+ * This is more specifically any non-preprocessor text.
+ */
+static int
+decl_function(struct parse *p, char *cp, size_t len)
+{
+	char		*ep, *ncp, *lcp, *rcp;
+	size_t		 nlen;
+	struct defn	*d;
+	struct decl	*e;
+
+	/* Fetch current interface definition. */
+	d = TAILQ_LAST(&p->dqhead, defnq);
+	assert(NULL != d);
+
+	/*
+	 * Since C tokens are semicolon-separated, we may be invoked any
+	 * number of times per a single line. 
+	 */
+again:
+	while (isspace((int)*cp)) {
+		cp++;
+		len--;
+	}
+	if ('\0' == *cp)
+		return(1);
+
+	/* Whether we're a continuation clause. */
+	if (d->multiline) {
+		/* This might be NULL if we're not a continuation. */
+		e = TAILQ_LAST(&d->dcqhead, declq);
+		assert(DECLTYPE_C == e->type);
+		assert(NULL != e);
+		assert(NULL != e->text);
+		assert(e->textsz);
+	} else {
+		assert(0 == d->instruct);
+		e = calloc(1, sizeof(struct decl));
+		e->type = DECLTYPE_C;
+		if (NULL == e)
+			err(EXIT_FAILURE, "%s:%zu: calloc", p->fn, p->ln);
+		TAILQ_INSERT_TAIL(&d->dcqhead, e, entries);
+	}
+
+	/*
+	 * We begin by seeing if there's a semicolon on this line.
+	 * If there is, we'll need to do some special handling.
+	 */
+	ep = strchr(cp, ';');
+	lcp = strchr(cp, '{');
+	rcp = strchr(cp, '}');
+
+	/* We're only a partial statement (i.e., no closure). */
+	if (NULL == ep && d->multiline) {
+		assert(NULL != e->text);
+		assert(e->textsz > 0);
+		/* Is a struct starting or ending here? */
+		if (d->instruct && NULL != rcp)
+			d->instruct--;
+		else if (NULL != lcp)
+			d->instruct++;
+		decl_function_add(p, &e->text, &e->textsz, cp, len);
+		return(1);
+	} else if (NULL == ep && ! d->multiline) {
+		d->multiline = 1;
+		/* Is a structure starting in this line? */
+		if (NULL != lcp && 
+		    (NULL == rcp || rcp < lcp))
+			d->instruct++;
+		decl_function_copy(p, &e->text, &e->textsz, cp, len);
+		return(1);
+	}
+
+	/* Position ourselves after the semicolon. */
+	assert(NULL != ep);
+	ncp = cp;
+	nlen = (ep - cp) + 1;
+	cp = ep + 1;
+	len -= nlen;
+
+	if (d->multiline) {
+		assert(NULL != e->text);
+		/* Don't stop the multi-line if we're in a struct. */
+		if (0 == d->instruct) {
+			if (NULL == lcp || lcp > cp)
+				d->multiline = 0;
+		} else if (NULL != rcp && rcp < cp)
+			if (0 == --d->instruct)
+				d->multiline = 0;
+		decl_function_add(p, &e->text, &e->textsz, ncp, nlen);
+	} else {
+		assert(NULL == e->text);
+		if (NULL != lcp && lcp < cp) {
+			d->multiline = 1;
+			d->instruct++;
+		}
+		decl_function_copy(p, &e->text, &e->textsz, ncp, nlen);
+	}
+
+	goto again;
+}
+
+/*
+ * A definition is just #define followed by space followed by the name,
+ * then the value of that name.
+ * We ignore the latter.
+ * FIXME: this does not understand multi-line CPP, but I don't think
+ * there are any instances of that in sqlite.h.
+ */
+static int
+decl_define(struct parse *p, char *cp, size_t len)
+{
+	struct defn	*d;
+	struct decl	*e;
+	size_t		 sz;
+
+	while (isspace((int)*cp)) {
+		cp++;
+		len--;
+	}
+	if (0 == len) {
+		warnx("%s:%zu: empty pre-processor "
+			"constant", p->fn, p->ln);
+		return(1);
+	}
+
+	d = TAILQ_LAST(&p->dqhead, defnq);
+	assert(NULL != d);
+
+	/*
+	 * We're parsing a preprocessor definition, but we're still
+	 * waiting on a semicolon from a function definition.
+	 * It might be a comment or an error.
+	 */
+	if (d->multiline) {
+		warnx("%s:%zu: multiline declaration "
+			"still open (harmless?)", p->fn, p->ln);
+		e = TAILQ_LAST(&d->dcqhead, declq);
+		assert(NULL != e);
+		e->type = DECLTYPE_NEITHER;
+		d->multiline = d->instruct = 0;
+	}
+
+	sz = 0;
+	while ('\0' != cp[sz] && ! isspace((int)cp[sz]))
+		sz++;
+
+	e = calloc(1, sizeof(struct decl));
+	if (NULL == e) 
+		err(EXIT_FAILURE, "%s:%zu: calloc", p->fn, p->ln);
+	e->type = DECLTYPE_CPP;
+	e->text = calloc(1, sz + 1);
+	if (NULL == e->text)
+		err(EXIT_FAILURE, "%s:%zu: calloc", p->fn, p->ln);
+	strlcpy(e->text, cp, sz + 1);
+	e->textsz = sz;
+	TAILQ_INSERT_TAIL(&d->dcqhead, e, entries);
+	return(1);
+}
+
+/*
+ * A declaration is a function, variable, preprocessor definition, or
+ * really anything else until we reach a blank line.
+ */
+static void
+decl(struct parse *p, char *cp, size_t len)
+{
+	struct defn	*d;
+	struct decl	*e;
+
+	while (isspace((int)*cp)) {
+		cp++;
+		len--;
+	}
+
+	/* Check closure. */
+	if ('\0' == *cp) {
+		p->phase = PHASE_INIT;
+		/* Check multiline status. */
+		d = TAILQ_LAST(&p->dqhead, defnq);
+		assert(NULL != d);
+		if (d->multiline) {
+			warnx("%s:%zu: multiline declaration "
+				"still open (harmless?)", p->fn, p->ln);
+			e = TAILQ_LAST(&d->dcqhead, declq);
+			assert(NULL != e);
+			e->type = DECLTYPE_NEITHER;
+			d->multiline = d->instruct = 0;
+		}
+		return;
+	} 
+	
+	/* 
+	 * Catch preprocessor defines, but discard all other types of
+	 * preprocessor statements.
+	 */
+	if ('#' == *cp) {
+		len--;
+		cp++;
+		while (isspace((int)*cp)) {
+			len--;
+			cp++;
+		}
+		if (0 == strncmp(cp, "define", 6))
+			decl_define(p, cp + 6, len - 6);
+		return;
+	}
+
+	decl_function(p, cp, len);
+}
+
+/*
+ * Parse "SEE ALSO" phrases, which can come at any point in the
+ * interface description (unlike what they claim).
+ */
+static void
+seealso(struct parse *p, char *cp, size_t len)
+{
+	struct defn	*d;
+
+	if ('\0' == *cp) {
+		warnx("%s:%zu: warn: unexpected end of "
+			"interface description", p->fn, p->ln);
+		p->phase = PHASE_INIT;
+		return;
+	} else if (0 == strcmp(cp, "*/")) {
+		p->phase = PHASE_DECL;
+		return;
+	} else if ('*' != cp[0] || '*' != cp[1]) {
+		warnx("%s:%zu: warn: unexpected end of "
+			"interface description", p->fn, p->ln);
+		p->phase = PHASE_INIT;
+		return;
+	}
+
+	cp += 2;
+	len -= 2;
+	while (isspace((int)*cp)) {
+		cp++;
+		len--;
+	}
+
+	/* Blank line: back to description part. */
+	if (0 == len) {
+		p->phase = PHASE_DESC;
+		return;
+	}
+
+	/* Fetch current interface definition. */
+	d = TAILQ_LAST(&p->dqhead, defnq);
+	assert(NULL != d);
+
+	d->seealso = realloc(d->seealso,
+		d->seealsosz + len + 1);
+	memcpy(d->seealso + d->seealsosz, cp, len);
+	d->seealsosz += len;
+	d->seealso[d->seealsosz] = '\0';
+}
+
+/*
+ * A definition description is a block of text that we'll later format
+ * in mdoc(7).
+ * It extends from the name of the definition down to the declarations
+ * themselves.
+ */
+static void
+desc(struct parse *p, char *cp, size_t len)
+{
+	struct defn	*d;
+	size_t		 nsz;
+
+	if ('\0' == *cp) {
+		warnx("%s:%zu: warn: unexpected end of "
+			"interface description", p->fn, p->ln);
+		p->phase = PHASE_INIT;
+		return;
+	} else if (0 == strcmp(cp, "*/")) {
+		/* End of comment area, start of declarations. */
+		p->phase = PHASE_DECL;
+		return;
+	} else if ('*' != cp[0] || '*' != cp[1]) {
+		warnx("%s:%zu: warn: unexpected end of "
+			"interface description", p->fn, p->ln);
+		p->phase = PHASE_INIT;
+		return;
+	}
+
+	cp += 2;
+	len -= 2;
+
+	while (isspace((int)*cp)) {
+		cp++;
+		len--;
+	}
+
+	/* Fetch current interface definition. */
+	d = TAILQ_LAST(&p->dqhead, defnq);
+	assert(NULL != d);
+
+	/* Ignore leading blank lines. */
+	if (0 == len && NULL == d->desc)
+		return;
+
+	/* Collect SEE ALSO clauses. */
+	if (0 == strncasecmp(cp, "see also:", 9)) {
+		cp += 9;
+		len -= 9;
+		while (isspace((int)*cp)) {
+			cp++;
+			len--;
+		}
+		p->phase = PHASE_SEEALSO;
+		d->seealso = realloc(d->seealso,
+			d->seealsosz + len + 1);
+		memcpy(d->seealso + d->seealsosz, cp, len);
+		d->seealsosz += len;
+		d->seealso[d->seealsosz] = '\0';
+		return;
+	}
+
+	/* White-space padding between lines. */
+	if (NULL != d->desc && 
+	    ' ' != d->desc[d->descsz - 1] &&
+	    '\n' != d->desc[d->descsz - 1]) {
+		d->desc = realloc(d->desc, d->descsz + 2);
+		if (NULL == d->desc)
+			err(EXIT_FAILURE, "%s:%zu: realloc", 
+				p->fn, p->ln);
+		d->descsz++;
+		strlcat(d->desc, " ", d->descsz + 1);
+	}
+
+	/* Either append the line of a newline, if blank. */
+	nsz = 0 == len ? 1 : len;
+	if (NULL == d->desc) {
+		d->desc = calloc(1, nsz + 1);
+		if (NULL == d->desc)
+			err(EXIT_FAILURE, "%s:%zu: calloc", 
+				p->fn, p->ln);
+	} else {
+		d->desc = realloc(d->desc, d->descsz + nsz + 1);
+		if (NULL == d->desc)
+			err(EXIT_FAILURE, "%s:%zu: realloc", 
+				p->fn, p->ln);
+	}
+	d->descsz += nsz;
+	strlcat(d->desc, 0 == len ? "\n" : cp, d->descsz + 1);
+}
+
+/*
+ * Copy all KEYWORDS into a buffer.
+ */
+static void
+keys(struct parse *p, char *cp, size_t len)
+{
+	struct defn	*d;
+
+	if ('\0' == *cp) {
+		warnx("%s:%zu: warn: unexpected end of "
+			"interface keywords", p->fn, p->ln);
+		p->phase = PHASE_INIT;
+		return;
+	} else if (0 == strcmp(cp, "*/")) {
+		/* End of comment area, start of declarations. */
+		p->phase = PHASE_DECL;
+		return;
+	} else if ('*' != cp[0] || '*' != cp[1]) {
+		if ('\0' != cp[1]) {
+			warnx("%s:%zu: warn: unexpected end of "
+				"interface keywords", p->fn, p->ln);
+			p->phase = PHASE_INIT;
+			return;
+		} else 
+			warnx("%s:%zu: warn: workaround in effect "
+				"for unexpected end of "
+				"interface keywords", p->fn, p->ln);
+	}
+
+	cp += 2;
+	len -= 2;
+	while (isspace((int)*cp)) {
+		cp++;
+		len--;
+	}
+
+	if (0 == len) {
+		p->phase = PHASE_DESC;
+		return;
+	} else if (strncmp(cp, "KEYWORDS:", 9)) 
+		return;
+
+	cp += 9;
+	len -= 9;
+
+	d = TAILQ_LAST(&p->dqhead, defnq);
+	assert(NULL != d);
+	d->keybuf = realloc(d->keybuf, d->keybufsz + len + 1);
+	if (NULL == d->keybuf)
+		err(EXIT_FAILURE, "%s:%zu: realloc", p->fn, p->ln);
+	memcpy(d->keybuf + d->keybufsz, cp, len);
+	d->keybufsz += len;
+	d->keybuf[d->keybufsz] = '\0';
+}
+
+/*
+ * Initial state is where we're scanning forward to find commented
+ * instances of CAPI3REF.
+ */
+static void
+init(struct parse *p, char *cp)
+{
+	struct defn	*d;
+
+	/* Look for comment hook. */
+	if ('*' != cp[0] || '*' != cp[1])
+		return;
+	cp += 2;
+	while (isspace((int)*cp))
+		cp++;
+
+	/* Look for beginning of definition. */
+	if (strncmp(cp, "CAPI3REF:", 9))
+		return;
+	cp += 9;
+	while (isspace((int)*cp))
+		cp++;
+	if ('\0' == *cp) {
+		warnx("%s:%zu: warn: unexpected end of "
+			"interface definition", p->fn, p->ln);
+		return;
+	}
+
+	/* Add definition to list of existing ones. */
+	d = calloc(1, sizeof(struct defn));
+	if (NULL == d)
+		err(EXIT_FAILURE, "%s:%zu: calloc", p->fn, p->ln);
+	d->name = strdup(cp);
+	if (NULL == d->name)
+		err(EXIT_FAILURE, "%s:%zu: strdup", p->fn, p->ln);
+	d->fn = p->fn;
+	d->ln = p->ln;
+	p->phase = PHASE_KEYS;
+	TAILQ_INIT(&d->dcqhead);
+	TAILQ_INSERT_TAIL(&p->dqhead, d, entries);
+}
+
+#define	BPOINT(_cp) \
+	(';' == (_cp)[0] || \
+	 '[' == (_cp)[0] || \
+	 ('(' == (_cp)[0] && '*' != (_cp)[1]) || \
+	 ')' == (_cp)[0] || \
+	 '{' == (_cp)[0])
+
+/*
+ * Given a declaration (be it preprocessor or C), try to parse out a
+ * reasonable "name" for the affair.
+ * For a struct, for example, it'd be the struct name.
+ * For a typedef, it'd be the type name.
+ * For a function, it'd be the function name.
+ */
+static void
+grok_name(const struct decl *e, 
+	const char **start, size_t *sz)
+{
+	const char	*cp;
+
+	*start = NULL;
+	*sz = 0;
+
+	if (DECLTYPE_CPP != e->type) {
+		assert(';' == e->text[e->textsz - 1]);
+		cp = e->text;
+		do {
+			while (isspace((int)*cp))
+				cp++;
+			if (BPOINT(cp))
+				break;
+			/* Function pointers... */
+			if ('(' == *cp)
+				cp++;
+			/* Pass over pointers. */
+			while ('*' == *cp)
+				cp++;
+			*start = cp;
+			*sz = 0;
+			while ( ! isspace((int)*cp)) {
+				if (BPOINT(cp))
+					break;
+				cp++;
+				(*sz)++;
+			}
+		} while ( ! BPOINT(cp));
+	} else {
+		*sz = e->textsz;
+		*start = e->text;
+	}
+}
+
+static int
+xrcmp(const void *p1, const void *p2)
+{
+	const char	*s1 = *(const char **)p1, 
+	     	 	*s2 = *(const char **)p2;
+
+	return(strcasecmp(s1, s2));
+}
+
+/*
+ * Extract information from the interface definition.
+ * Mark it as "postprocessed" on success.
+ */
+static void
+postprocess(const char *prefix, struct defn *d)
+{
+	struct decl	*first;
+	const char	*start;
+	size_t		 offs, sz, i;
+	ENTRY		 ent;
+
+	if (TAILQ_EMPTY(&d->dcqhead))
+		return;
+
+	/* Find the first #define or declaration. */
+	TAILQ_FOREACH(first, &d->dcqhead, entries)
+		if (DECLTYPE_CPP == first->type ||
+		    DECLTYPE_C == first->type)
+			break;
+
+	if (NULL == first) {
+		warnx("%s:%zu: no entry to document", d->fn, d->ln);
+		return;
+	}
+
+	/* 
+	 * Now compute the document name (`Dt').
+	 * We'll also use this for the filename.
+	 */
+	grok_name(first, &start, &sz);
+	if (NULL == start) {
+		warnx("%s:%zu: couldn't deduce "
+			"entry name", d->fn, d->ln);
+		return;
+	}
+
+	/* Document name needs all-caps. */
+	d->dt = malloc(sz + 1);
+	if (NULL == d->dt)
+		err(EXIT_FAILURE, "malloc");
+	memcpy(d->dt, start, sz);
+	d->dt[sz] = '\0';
+	for (i = 0; i < sz; i++)
+		d->dt[i] = toupper((int)d->dt[i]);
+
+	/* Filename needs no special chars. */
+	asprintf(&d->fname, "%s/%.*s.3", 
+		prefix, (int)sz, start);
+	if (NULL == d->fname)
+		err(EXIT_FAILURE, "asprintf");
+
+	offs = strlen(prefix) + 1;
+	for (i = 0; i < sz; i++) {
+		if (isalnum((int)d->fname[offs + i]) ||
+		    '_' == d->fname[offs + i] ||
+		    '-' == d->fname[offs + i])
+			continue;
+		d->fname[offs + i] = '_';
+	}
+
+	/* 
+	 * First, extract all keywords.
+	 */
+	for (i = 0; i < d->keybufsz; ) {
+		while (isspace((int)d->keybuf[i]))
+			i++;
+		if (i == d->keybufsz)
+			break;
+		sz = 0;
+		start = &d->keybuf[i];
+		if ('{' == d->keybuf[i]) {
+			start = &d->keybuf[++i];
+			for ( ; i < d->keybufsz; i++, sz++) 
+				if ('}' == d->keybuf[i])
+					break;
+			if ('}' == d->keybuf[i])
+				i++;
+		} else
+			for ( ; i < d->keybufsz; i++, sz++)
+				if (isspace((int)d->keybuf[i]))
+					break;
+		if (0 == sz)
+			continue;
+		d->keys = realloc(d->keys,
+			(d->keysz + 1) * sizeof(char *));
+		if (NULL == d->keys) 
+			err(EXIT_FAILURE, "realloc");
+		d->keys[d->keysz] = malloc(sz + 1);
+		if (NULL == d->keys[d->keysz]) 
+			err(EXIT_FAILURE, "malloc");
+		memcpy(d->keys[d->keysz], start, sz);
+		d->keys[d->keysz][sz] = '\0';
+		d->keysz++;
+		
+		/* Hash the keyword. */
+		ent.key = d->keys[d->keysz - 1];
+		ent.data = d;
+		(void)hsearch(ent, ENTER);
+	}
+
+	/*
+	 * Now extract all `Nm' values for this document.
+	 * We only use CPP and C references, and hope for the best when
+	 * doing so.
+	 * Enter each one of these as a searchable keyword.
+	 */
+	TAILQ_FOREACH(first, &d->dcqhead, entries) {
+		if (DECLTYPE_CPP != first->type &&
+		    DECLTYPE_C != first->type)
+			continue;
+		grok_name(first, &start, &sz);
+		if (NULL == start) 
+			continue;
+		d->nms = realloc(d->nms, 
+			(d->nmsz + 1) * sizeof(char *));
+		if (NULL == d->nms)
+			err(EXIT_FAILURE, "realloc");
+		d->nms[d->nmsz] = malloc(sz + 1);
+		if (NULL == d->nms[d->nmsz])
+			err(EXIT_FAILURE, "malloc");
+		memcpy(d->nms[d->nmsz], start, sz);
+		d->nms[d->nmsz][sz] = '\0';
+		d->nmsz++;
+
+		/* Hash the name. */
+		ent.key = d->nms[d->nmsz - 1];
+		ent.data = d;
+		(void)hsearch(ent, ENTER);
+	}
+
+	if (0 == d->nmsz) {
+		warnx("%s:%zu: couldn't deduce "
+			"any names", d->fn, d->ln);
+		return;
+	}
+
+	/*
+	 * Next, scan for all `Xr' values.
+	 * We'll add more to this list later.
+	 */
+	for (i = 0; i < d->seealsosz; i++) {
+		/* 
+		 * Find next value starting with `['.
+		 * There's other stuff in there (whitespace or
+		 * free text leading up to these) that we're ok
+		 * to ignore.
+		 */
+		while (i < d->seealsosz && '[' != d->seealso[i])
+			i++;
+		if (i == d->seealsosz)
+			break;
+
+		/* 
+		 * Now scan for the matching `]'.
+		 * We can also have a vertical bar if we're separating a
+		 * keyword and its shown name.
+		 */
+		start = &d->seealso[++i];
+		sz = 0;
+		while (i < d->seealsosz &&
+		      ']' != d->seealso[i] &&
+		      '|' != d->seealso[i]) {
+			i++;
+			sz++;
+		}
+		if (i == d->seealsosz)
+			break;
+		if (0 == sz)
+			continue;
+
+		/* 
+		 * Continue on to the end-of-reference, if we weren't
+		 * there to begin with.
+		 */
+		if (']' != d->seealso[i]) 
+			while (i < d->seealsosz &&
+			      ']' != d->seealso[i])
+				i++;
+
+		/* Strip trailing whitespace. */
+		while (sz > 1 && ' ' == start[sz - 1])
+			sz--;
+
+		/* Strip trailing parenthesis. */
+		if (sz > 2 && 
+		    '(' == start[sz - 2] && 
+	 	    ')' == start[sz - 1])
+			sz -= 2;
+
+		d->xrs = realloc(d->xrs,
+			(d->xrsz + 1) * sizeof(char *));
+		if (NULL == d->xrs)
+			err(EXIT_FAILURE, "realloc");
+		d->xrs[d->xrsz] = malloc(sz + 1);
+		if (NULL == d->xrs[d->xrsz])
+			err(EXIT_FAILURE, "malloc");
+		memcpy(d->xrs[d->xrsz], start, sz);
+		d->xrs[d->xrsz][sz] = '\0';
+		d->xrsz++;
+	}
+
+	/*
+	 * Next, extract all references.
+	 * We'll accumulate these into a list of SEE ALSO tags, after.
+	 * See how these are parsed above for a description: this is
+	 * basically the same thing.
+	 */
+	for (i = 0; i < d->descsz; i++) {
+		if ('[' != d->desc[i])
+			continue;
+		i++;
+		if ('[' == d->desc[i])
+			continue;
+
+		start = &d->desc[i];
+		for (sz = 0; i < d->descsz; i++, sz++)
+			if (']' == d->desc[i] ||
+			    '|' == d->desc[i])
+				break;
+
+		if (i == d->descsz)
+			break;
+		else if (sz == 0)
+			continue;
+
+		if (']' != d->desc[i]) 
+			while (i < d->descsz &&
+			      ']' != d->desc[i])
+				i++;
+
+		while (sz > 1 && ' ' == start[sz - 1])
+			sz--;
+
+		if (sz > 2 && 
+		    '(' == start[sz - 2] &&
+		    ')' == start[sz - 1])
+			sz -= 2;
+
+		d->xrs = realloc(d->xrs,
+			(d->xrsz + 1) * sizeof(char *));
+		if (NULL == d->xrs)
+			err(EXIT_FAILURE, "realloc");
+		d->xrs[d->xrsz] = malloc(sz + 1);
+		if (NULL == d->xrs[d->xrsz])
+			err(EXIT_FAILURE, "malloc");
+		memcpy(d->xrs[d->xrsz], start, sz);
+		d->xrs[d->xrsz][sz] = '\0';
+		d->xrsz++;
+	}
+
+	qsort(d->xrs, d->xrsz, sizeof(char *), xrcmp);
+	d->postprocessed = 1;
+}
+
+/*
+ * Convenience function to look up a keyword.
+ * Returns the keyword's file if found or NULL.
+ */
+static const char *
+lookup(char *key)
+{
+	ENTRY		 ent;
+	ENTRY		*res;
+	struct defn	*d;
+
+	ent.key = key;
+	res = hsearch(ent, FIND);
+	if (NULL == res) 
+		return(NULL);
+	d = (struct defn *)res->data;
+	if (0 == d->nmsz)
+		return(NULL);
+	assert(NULL != d->nms[0]);
+	return(d->nms[0]);
+}
+
+/*
+ * Emit a valid mdoc(7) document within the given prefix.
+ */
+static void
+emit(const struct defn *d)
+{
+	struct decl	*first;
+	size_t		 sz, i, col, last, ns;
+	FILE		*f;
+	char		*cp;
+	const char	*res, *lastres, *args, *str, *end;
+	enum tag	 tag;
+	enum preproc	 pre;
+
+	if ( ! d->postprocessed) {
+		warnx("%s:%zu: interface has errors, not "
+			"producing manpage", d->fn, d->ln);
+		return;
+	}
+
+	if (0 == nofile) {
+		if (NULL == (f = fopen(d->fname, "w"))) {
+			warn("%s: fopen", d->fname);
+			return;
+		}
+	} else
+		f = stdout;
+
+	/* Begin by outputting the mdoc(7) header. */
+	fputs(".Dd $" "Mdocdate$\n", f);
+	fprintf(f, ".Dt %s 3\n", d->dt);
+	fputs(".Os\n", f);
+	fputs(".Sh NAME\n", f);
+
+	/* Now print the name bits of each declaration. */
+	for (i = 0; i < d->nmsz; i++)
+		fprintf(f, ".Nm %s%s\n", d->nms[i], 
+			i < d->nmsz - 1 ? " ," : "");
+
+	fprintf(f, ".Nd %s\n", d->name);
+	fputs(".Sh SYNOPSIS\n", f);
+
+	TAILQ_FOREACH(first, &d->dcqhead, entries) {
+		if (DECLTYPE_CPP != first->type &&
+		    DECLTYPE_C != first->type)
+			continue;
+
+		/* Easy: just print the CPP name. */
+		if (DECLTYPE_CPP == first->type) {
+			fprintf(f, ".Fd #define %s\n",
+				first->text);
+			continue;
+		}
+
+		/* First, strip out the sqlite CPPs. */
+		for (i = 0; i < first->textsz; ) {
+			for (pre = 0; pre < PREPROC__MAX; pre++) {
+				sz = strlen(preprocs[pre]);
+				if (strncmp(preprocs[pre], 
+				    &first->text[i], sz))
+					continue;
+				i += sz;
+				while (isspace((int)first->text[i]))
+					i++;
+				break;
+			}
+			if (pre == PREPROC__MAX)
+				break;
+		}
+
+		/* If we're a typedef, immediately print Vt. */
+		if (0 == strncmp(&first->text[i], "typedef", 7)) {
+			fprintf(f, ".Vt %s\n", &first->text[i]);
+			continue;
+		}
+
+		/* Are we a struct? */
+		if (first->textsz > 2 && 
+		    '}' == first->text[first->textsz - 2] &&
+		    NULL != (cp = strchr(&first->text[i], '{'))) {
+			*cp = '\0';
+			fprintf(f, ".Vt %s;\n", &first->text[i]);
+			/* Restore brace for later usage. */
+			*cp = '{';
+			continue;
+		}
+
+		/* Catch remaining non-functions. */
+		if (first->textsz > 2 &&
+		    ')' != first->text[first->textsz - 2]) {
+			fprintf(f, ".Vt %s\n", &first->text[i]);
+			continue;
+		}
+
+		str = &first->text[i];
+		if (NULL == (args = strchr(str, '('))) {
+			/* What is this? */
+			fputs(".Bd -literal\n", f);
+			fputs(&first->text[i], f);
+			fputs("\n.Ed\n", f);
+			continue;
+		}
+
+		/* Scroll back to end of function name. */
+		end = args - 1;
+		while (end > str && isspace((int)*end))
+			end--;
+
+		/* Scroll back to what comes before. */
+		for ( ; end > str; end--)
+			if (isspace((int)*end) || '*' == *end)
+				break;
+
+		/* 
+		 * If we can't find what came before, then the function
+		 * has no type, which is odd... let's just call it void.
+		 */
+		if (end > str) {
+			fprintf(f, ".Ft %.*s\n", 
+				(int)(end - str + 1), str);
+			fprintf(f, ".Fo %.*s\n", 
+				(int)(args - end - 1), end + 1);
+		} else {
+			fputs(".Ft void\n", f);
+			fprintf(f, ".Fo %.*s\n", (int)(args - end), end);
+		}
+
+		/*
+		 * Convert function arguments into `Fa' clauses.
+		 * This also handles nested function pointers, which
+		 * would otherwise throw off the delimeters.
+		 */
+		for (;;) {
+			str = ++args;
+			while (isspace((int)*str))
+				str++;
+			fputs(".Fa \"", f);
+			ns = 0;
+			while ('\0' != *str && 
+			       (ns || ',' != *str) && 
+			       (ns || ')' != *str)) {
+				if ('/' == str[0] && '*' == str[1]) {
+					str += 2;
+					for ( ; '\0' != str[0]; str++)
+						if ('*' == str[0] && '/' == str[1])
+							break;
+					if ('\0' == *str)
+						break;
+					str += 2;
+					while (isspace((int)*str))
+						str++;
+					if ('\0' == *str ||
+					    (0 == ns && ',' == *str) ||
+					    (0 == ns && ')' == *str))
+						break;
+				}
+				if ('(' == *str)
+					ns++;
+				else if (')' == *str)
+					ns--;
+				fputc(*str, f);
+				str++;
+			}
+			fputs("\"\n", f);
+			if ('\0' == *str || ')' == *str)
+				break;
+			args = str;
+		}
+
+		fputs(".Fc\n", f);
+	}
+
+	fputs(".Sh DESCRIPTION\n", f);
+
+	/* 
+	 * Strip the crap out of the description.
+	 * "Crap" consists of things I don't understand that mess up
+	 * parsing of the HTML, for instance,
+	 *   <dl>[[foo bar]]<dt>foo bar</dt>...</dl>
+	 * These are not well-formed HTML.
+	 */
+	for (i = 0; i < d->descsz; i++) {
+		if ('^' == d->desc[i] && 
+		    '(' == d->desc[i + 1]) {
+			d->desc[i] = d->desc[i + 1] = ' ';
+			i++;
+			continue;
+		} else if (')' == d->desc[i] && 
+			   '^' == d->desc[i + 1]) {
+			d->desc[i] = d->desc[i + 1] = ' ';
+			i++;
+			continue;
+		} else if ('^' == d->desc[i]) {
+			d->desc[i] = ' ';
+			continue;
+		} else if ('[' != d->desc[i] || 
+			   '[' != d->desc[i + 1]) 
+			continue;
+		d->desc[i] = d->desc[i + 1] = ' ';
+		for (i += 2; i < d->descsz; i++) {
+			if (']' == d->desc[i] && 
+			    ']' == d->desc[i + 1]) 
+				break;
+			d->desc[i] = ' ';
+		}
+		if (i == d->descsz)
+			continue;
+		d->desc[i] = d->desc[i + 1] = ' ';
+		i++;
+	}
+
+	/*
+	 * Here we go!
+	 * Print out the description as best we can.
+	 * Do on-the-fly processing of any HTML we encounter into
+	 * mdoc(7) and try to break lines up.
+	 */
+	col = 0;
+	for (i = 0; i < d->descsz; ) {
+		/* 
+		 * Newlines are paragraph breaks.
+		 * If we have multiple newlines, then keep to a single
+		 * `Pp' to keep it clean.
+		 * Only do this if we're not before a block-level HTML,
+		 * as this would mean, for instance, a `Pp'-`Bd' pair.
+		 */
+		if ('\n' == d->desc[i]) {
+			while (isspace((int)d->desc[i]))
+				i++;
+			for (tag = 0; tag < TAG__MAX; tag++) {
+				sz = strlen(tags[tag].html);
+				if (0 == strncmp(&d->desc[i], tags[tag].html, sz))
+					break;
+			}
+			if (TAG__MAX == tag ||
+			    TAGINFO_INLINE & tags[tag].flags) {
+				if (col > 0)
+					fputs("\n", f);
+				fputs(".Pp\n", f);
+				/* We're on a new line. */
+				col = 0;
+			}
+			continue;
+		}
+
+		/*
+		 * New sentence, new line.
+		 * We guess whether this is the case by using the
+		 * dumbest possible heuristic.
+		 */
+		if (' ' == d->desc[i] && i &&
+		    '.' == d->desc[i - 1]) {
+			while (' ' == d->desc[i])
+				i++;
+			fputs("\n", f);
+			col = 0;
+			continue;
+		}
+		/*
+		 * After 65 characters, force a break when we encounter
+		 * white-space to keep our lines more or less tidy.
+		 */
+		if (col > 65 && ' ' == d->desc[i]) {
+			while (' ' == d->desc[i]) 
+				i++;
+			fputs("\n", f);
+			col = 0;
+			continue;
+		}
+
+		/*
+		 * Parsing HTML tags.
+		 * Why, sqlite guys, couldn't you have used something
+		 * like markdown or something?  
+		 * Sheesh.
+		 */
+		if ('<' == d->desc[i]) {
+			for (tag = 0; tag < TAG__MAX; tag++) {
+				sz = strlen(tags[tag].html);
+				if (strncmp(&d->desc[i], 
+				    tags[tag].html, sz))
+					continue;
+				/*
+				 * NOOP tags don't do anything, such as
+				 * the case of `</dd>', which only
+				 * serves to end an `It' block that will
+				 * be closed out by a subsequent `It' or
+				 * end of clause `El' anyway.
+				 * Skip the trailing space.
+				 */
+				if (TAGINFO_NOOP & tags[tag].flags) {
+					i += sz;
+					while (isspace((int)d->desc[i]))
+						i++;
+					break;
+				} else if (TAGINFO_INLINE & tags[tag].flags) {
+					fputs(tags[tag].mdoc, f);
+					i += sz;
+					break;
+				}
+
+				/* 
+				 * A breaking mdoc(7) statement.
+				 * Break the current line, output the
+				 * macro, and conditionally break
+				 * following that (or we might do
+				 * nothing at all).
+				 */
+				if (col > 0) {
+					fputs("\n", f);
+					col = 0;
+				}
+				fputs(tags[tag].mdoc, f);
+				if ( ! (TAGINFO_NOBR & tags[tag].flags)) {
+					fputs("\n", f);
+					col = 0;
+				} else if ( ! (TAGINFO_NOSP & tags[tag].flags)) {
+					fputs(" ", f);
+					col++;
+				}
+				i += sz;
+				while (isspace((int)d->desc[i]))
+					i++;
+				break;
+			}
+			if (tag < TAG__MAX)
+				continue;
+		} else if ('[' == d->desc[i] && 
+			   ']' != d->desc[i + 1]) {
+			/* Do we start at the bracket or bar? */
+			for (sz = i + 1; sz < d->descsz; sz++) 
+				if ('|' == d->desc[sz] ||
+				    ']' == d->desc[sz])
+					break;
+
+			if (sz == d->descsz)
+				continue;
+			else if ('|' == d->desc[sz])
+				i = sz + 1;
+			else
+				i = i + 1;
+
+			/*
+			 * Now handle in-page references.
+			 * Print them out as-is: we've already
+			 * accumulated them into our "SEE ALSO" values,
+			 * which we'll use below.
+			 */
+			for ( ; i < d->descsz; i++, col++) {
+				if (']' == d->desc[i]) {
+					i++;
+					break;
+				}
+				fputc(d->desc[i], f);
+				col++;
+			}
+			continue;
+		}
+
+		if (' ' == d->desc[i] && 0 == col) {
+			while (' ' == d->desc[i])
+				i++;
+			continue;
+		}
+
+		assert('\n' != d->desc[i]);
+
+		/*
+		 * Handle some oddities.
+		 * The following HTML escapes exist in the output that I
+		 * could find.
+		 * There might be others...
+		 */
+		if (0 == strncmp(&d->desc[i], "&nbsp;", 6)) {
+			i += 6;
+			fputc(' ', f);
+		} else if (0 == strncmp(&d->desc[i], "&lt;", 4)) {
+			i += 4;
+			fputc('<', f);
+		} else if (0 == strncmp(&d->desc[i], "&gt;", 4)) {
+			i += 4;
+			fputc('>', f);
+		} else if (0 == strncmp(&d->desc[i], "&#91;", 5)) {
+			i += 5;
+			fputc('[', f);
+		} else {
+			/* Make sure we don't trigger a macro. */
+			if (0 == col && '.' == d->desc[i])
+				fputs("\\&", f);
+			fputc(d->desc[i], f);
+			i++;
+		}
+
+		col++;
+	}
+
+	if (col > 0)
+		fputs("\n", f);
+
+	if (d->xrsz > 0) {
+		/*
+		 * Look up all of our keywords (which are in the xrs
+		 * field) in the table of all known keywords.
+		 * Don't print duplicates.
+		 */
+		lastres = NULL;
+		for (last = 0, i = 0; i < d->xrsz; i++) {
+			res = lookup(d->xrs[i]);
+			/* Ignore self-reference. */
+			if (res == d->nms[0] && verbose) 
+				warnx("%s:%zu: self-reference: %s",
+					d->fn, d->ln, d->xrs[i]);
+			if (res == d->nms[0] && verbose) 
+				continue;
+			if (NULL == res && verbose) 
+				warnx("%s:%zu: ref not found: %s",  
+					d->fn, d->ln, d->xrs[i]);
+			if (NULL == res)
+				continue;
+
+			/* Ignore duplicates. */
+			if (NULL != lastres && lastres == res)
+				continue;
+			if (last)
+				fputs(" ,\n", f);
+			else
+				fputs(".Sh SEE ALSO\n", f);
+			fprintf(f, ".Xr %s 3", res);
+			last = 1;
+			lastres = res;
+		}
+		if (last)
+			fputs("\n", f);
+	}
+
+	if (0 == nofile)
+		fclose(f);
+}
+
+int
+main(int argc, char *argv[])
+{
+	size_t		 i, len;
+	FILE		*f;
+	char		*cp;
+	const char	*prefix;
+	struct parse	 p;
+	int		 rc, ch;
+	struct defn	*d;
+	struct decl	*e;
+
+	rc = 0;
+	prefix = ".";
+	f = stdin;
+	memset(&p, 0, sizeof(struct parse));
+	p.fn = "<stdin>";
+	p.ln = 0;
+	p.phase = PHASE_INIT;
+	TAILQ_INIT(&p.dqhead);
+
+	while (-1 != (ch = getopt(argc, argv, "np:v")))
+		switch (ch) {
+		case ('n'):
+			nofile = 1;
+			break;
+		case ('p'):
+			prefix = optarg;
+			break;
+		case ('v'):
+			verbose = 1;
+			break;
+		default:
+			goto usage;
+		}
+
+	/*
+	 * Read in line-by-line and process in the phase dictated by our
+	 * finite state automaton.
+	 */
+	while (NULL != (cp = fgetln(f, &len))) {
+		assert(len > 0);
+		p.ln++;
+		if ('\n' != cp[len - 1]) {
+			warnx("%s:%zu: unterminated line", p.fn, p.ln);
+			break;
+		}
+		cp[--len] = '\0';
+		/* Lines are always nil-terminated. */
+		switch (p.phase) {
+		case (PHASE_INIT):
+			init(&p, cp);
+			break;
+		case (PHASE_KEYS):
+			keys(&p, cp, len);
+			break;
+		case (PHASE_DESC):
+			desc(&p, cp, len);
+			break;
+		case (PHASE_SEEALSO):
+			seealso(&p, cp, len);
+			break;
+		case (PHASE_DECL):
+			decl(&p, cp, len);
+			break;
+		}
+	}
+
+	/*
+	 * If we hit the last line, then try to process.
+	 * Otherwise, we failed along the way.
+	 */
+	if (NULL == cp) {
+		/* 
+		 * Allow us to be at the declarations or scanning for
+		 * the next clause.
+		 */
+		if (PHASE_INIT == p.phase ||
+		    PHASE_DECL == p.phase) {
+			if (0 == hcreate(5000))
+				err(EXIT_FAILURE, "hcreate");
+			TAILQ_FOREACH(d, &p.dqhead, entries)
+				postprocess(prefix, d);
+			TAILQ_FOREACH(d, &p.dqhead, entries)
+				emit(d);
+			rc = 1;
+		} else if (PHASE_DECL != p.phase)
+			warnx("%s:%zu: exit when not in "
+				"initial state", p.fn, p.ln);
+	}
+
+	while ( ! TAILQ_EMPTY(&p.dqhead)) {
+		d = TAILQ_FIRST(&p.dqhead);
+		TAILQ_REMOVE(&p.dqhead, d, entries);
+		while ( ! TAILQ_EMPTY(&d->dcqhead)) {
+			e = TAILQ_FIRST(&d->dcqhead);
+			TAILQ_REMOVE(&d->dcqhead, e, entries);
+			free(e->text);
+			free(e);
+		}
+		free(d->name);
+		free(d->desc);
+		free(d->dt);
+		for (i = 0; i < d->nmsz; i++)
+			free(d->nms[i]);
+		for (i = 0; i < d->xrsz; i++)
+			free(d->xrs[i]);
+		for (i = 0; i < d->keysz; i++)
+			free(d->keys[i]);
+		free(d->keys);
+		free(d->nms);
+		free(d->xrs);
+		free(d->fname);
+		free(d->seealso);
+		free(d->keybuf);
+		free(d);
+	}
+
+	return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
+usage:
+	fprintf(stderr, "usage: %s [-nv] [-p prefix]\n", getprogname());
+	return(EXIT_FAILURE);
+}
Index: src/external/public-domain/sqlite/sqlite2mdoc/sqlite2mdoc.1
diff -u /dev/null src/external/public-domain/sqlite/sqlite2mdoc/sqlite2mdoc.1:1.1
--- /dev/null	Wed Mar 30 17:30:21 2016
+++ src/external/public-domain/sqlite/sqlite2mdoc/sqlite2mdoc.1	Wed Mar 30 17:30:20 2016
@@ -0,0 +1,129 @@
+.Dd $Mdocdate: March 28 2016 $
+.Dt SQLITE2MDOC 1
+.Os
+.Sh NAME
+.Nm sqlite2mdoc
+.Nd extract C reference manpages from SQLite header file
+.Sh SYNOPSIS
+.Nm sqlite2mdoc
+.Op Fl nv
+.Op Fl p Ar prefix
+.Sh DESCRIPTION
+The
+.Nm
+utility extracts C interface descriptions from a header file composed
+using the SQLite documentation syntax (see
+.Sx SYNTAX ) .
+It then emits the corresponding set of
+.Xr mdoc 7
+manpages, one per interface description.
+By default, 
+.Nm
+reads from standard input and outputs files into the current directory.
+Its arguments are as follows:
+.Bl -tag -width Ds
+.It Fl n
+Don't create files in
+.Ar prefix :
+just dump everything to stdout.
+.It Fl v
+Show all missing references.
+.It Fl p Ar prefix
+Output into
+.Ar prefix ,
+which must already exist.
+.El
+.Pp
+This tool was designed for SQLite's header file
+.Pa sqlite.h ,
+although it will work for any document following the
+.Sx SYNTAX .
+Historically, all of the SQLite C reference documentation (called the
+.Dq C-language Interface Specification )
+was only available in HTML.
+.Sh SYNTAX
+The syntax for the interface descriptions is as follows:
+.Bd -literal
+/*
+** CAPI3REF: A Brief Description Here
+** KEYWORDS: {multiword keyword} single_keyword
+**
+** This is a longer description following the blank line.
+** You can refer to [other descriptions] by enclosing references to
+** keywords (or declaration names) in brackets.
+** If the brackets have [some_keyword | a bar] within them, the
+** second part is shown while the first is used as a reference.
+** Each reference will be collected and its corresponding manpage,
+** if found, will be listed in SEE ALSO.
+**
+** You can embed <b>HTML</b>, but you must limit this to lists
+** (ordered, unordered, description).
+**
+** See also: [another_keyword], although since we're the only
+** reference, none of these will be found.
+** Anything not in [function() | quotes] will be discarded.
+**
+** Now we end the comment.  Anything after this, until the next
+** blank line, is taken as declarations.
+*/
+int function_name(char *anarg, char *another);
+#define A_DEFINITION "text"
+struct a_struct {
+	struct embedded_struct {
+		int foo;
+		int bar;
+	};
+};
+typedef void (*fptr)(const char *a, const char *b);
+.Ed
+.Pp
+It's important that the opening and closing comment parts are flush left
+and not followed by spaces.
+Paragraph breaks (empty comment lines) should also not have any spaces.
+.Pp
+There are many SQLite-specific bits that are purposely not documented so
+that people don't use them.
+.\" .Sh CONTEXT
+.\" For section 9 functions only.
+.\" .Sh IMPLEMENTATION NOTES
+.\" Not used in OpenBSD.
+.\" .Sh RETURN VALUES
+.\" For sections 2, 3, and 9 function return values only.
+.\" .Sh ENVIRONMENT
+.\" For sections 1, 6, 7, and 8 only.
+.\" .Sh FILES
+.Sh EXIT STATUS
+.Ex -std
+.\" For sections 1, 6, and 8 only.
+.\" .Sh EXAMPLES
+.\" .Sh DIAGNOSTICS
+.\" For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
+.\" .Sh ERRORS
+.\" For sections 2, 3, 4, and 9 errno settings only.
+.Sh SEE ALSO
+.Xr mdoc 7 ,
+.Lk https://www.sqlite.org/capi3ref.html "C-language Interface Specification for SQLite"
+.\" .Sh STANDARDS
+.\" .Sh HISTORY
+.Sh AUTHORS
+.Nm
+was written by
+.An Kristaps Dzonsons Aq Mt [email protected] .
+.Sh CAVEATS
+There are some missing bits:
+.Bl -bullet
+.It
+HTML tables are not parsed.
+.It
+HTML anchors are not parsed.
+.It
+Obviously-broken HTML (e.g., unmatched </dt>) isn't worked around.
+.It
+Structure definitions are discarded except for the structure name.
+.It
+Comments in definitions will cause extra white-space.
+.El
+.Sh BUGS
+Absolutely.
+.\" .Sh SECURITY CONSIDERATIONS
+.\" Not used in OpenBSD.

Reply via email to