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], " ", 6)) {
+ i += 6;
+ fputc(' ', f);
+ } else if (0 == strncmp(&d->desc[i], "<", 4)) {
+ i += 4;
+ fputc('<', f);
+ } else if (0 == strncmp(&d->desc[i], ">", 4)) {
+ i += 4;
+ fputc('>', f);
+ } else if (0 == strncmp(&d->desc[i], "[", 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.