Wed Mar 30 21:30:21 2016 UTC ()
From: https://github.com/kristapsdz/sqlite2mdoc


(christos)
diff -r0 -r1.1 src/external/public-domain/sqlite/sqlite2mdoc/LICENSE.md
diff -r0 -r1.1 src/external/public-domain/sqlite/sqlite2mdoc/Makefile
diff -r0 -r1.1 src/external/public-domain/sqlite/sqlite2mdoc/README.md
diff -r0 -r1.1 src/external/public-domain/sqlite/sqlite2mdoc/main.c
diff -r0 -r1.1 src/external/public-domain/sqlite/sqlite2mdoc/sqlite2mdoc.1

File Added: src/external/public-domain/sqlite/sqlite2mdoc/LICENSE.md
Copyright (c) 2012--2016, Kristaps Dzonsons <kristaps@bsd.lv>

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.


File Added: src/external/public-domain/sqlite/sqlite2mdoc/Makefile
# 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

File Added: src/external/public-domain/sqlite/sqlite2mdoc/README.md
## 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.

File Added: src/external/public-domain/sqlite/sqlite2mdoc/main.c
/*	$Id: main.c,v 1.1 2016/03/30 21:30:20 christos Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *
 * 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);
}

File Added: src/external/public-domain/sqlite/sqlite2mdoc/sqlite2mdoc.1
.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 kristaps@bsd.lv .
.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.