+ free(secname);
+ return(1);
+}
+
+static int
+post_ignpar(POST_ARGS)
+{
+ struct mdoc_node *np;
+
+ if (MDOC_BODY != mdoc->last->type)
+ return(1);
+
+ if (NULL != (np = mdoc->last->child))
+ if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
+ mandoc_vmsg(MANDOCERR_PAR_SKIP,
+ mdoc->parse, np->line, np->pos,
+ "%s after %s", mdoc_macronames[np->tok],
+ mdoc_macronames[mdoc->last->tok]);
+ mdoc_node_delete(mdoc, np);
+ }
+
+ if (NULL != (np = mdoc->last->last))
+ if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
+ mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
+ np->line, np->pos, "%s at the end of %s",
+ mdoc_macronames[np->tok],
+ mdoc_macronames[mdoc->last->tok]);
+ mdoc_node_delete(mdoc, np);
+ }
+
+ return(1);
+}
+
+static int
+pre_par(PRE_ARGS)
+{
+
+ if (NULL == mdoc->last)
+ return(1);
+ if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
+ return(1);
+
+ /*
+ * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
+ * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
+ */
+
+ if (MDOC_Pp != mdoc->last->tok &&
+ MDOC_Lp != mdoc->last->tok &&
+ MDOC_br != mdoc->last->tok)
+ return(1);
+ if (MDOC_Bl == n->tok && n->norm->Bl.comp)
+ return(1);
+ if (MDOC_Bd == n->tok && n->norm->Bd.comp)
+ return(1);
+ if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
+ return(1);
+
+ mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
+ mdoc->last->line, mdoc->last->pos,
+ "%s before %s", mdoc_macronames[mdoc->last->tok],
+ mdoc_macronames[n->tok]);
+ mdoc_node_delete(mdoc, mdoc->last);
+ return(1);
+}
+
+static int
+post_par(POST_ARGS)
+{
+ struct mdoc_node *np;
+
+ if (MDOC_ELEM != mdoc->last->type &&
+ MDOC_BLOCK != mdoc->last->type)
+ return(1);
+
+ if (NULL == (np = mdoc->last->prev)) {
+ np = mdoc->last->parent;
+ if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
+ return(1);
+ } else {
+ if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
+ (MDOC_br != mdoc->last->tok ||
+ (MDOC_sp != np->tok && MDOC_br != np->tok)))
+ return(1);
+ }
+
+ mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
+ mdoc->last->line, mdoc->last->pos,
+ "%s after %s", mdoc_macronames[mdoc->last->tok],
+ mdoc_macronames[np->tok]);
+ mdoc_node_delete(mdoc, mdoc->last);
+ return(1);
+}
+
+static int
+pre_literal(PRE_ARGS)
+{
+
+ if (MDOC_BODY != n->type)
+ return(1);
+
+ /*
+ * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
+ * -unfilled' macros set MDOC_LITERAL on entrance to the body.
+ */
+
+ switch (n->tok) {
+ case MDOC_Dl:
+ mdoc->flags |= MDOC_LITERAL;
+ break;
+ case MDOC_Bd:
+ if (DISP_literal == n->norm->Bd.type)
+ mdoc->flags |= MDOC_LITERAL;
+ if (DISP_unfilled == n->norm->Bd.type)
+ mdoc->flags |= MDOC_LITERAL;
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+
+ return(1);
+}
+
+static int
+post_dd(POST_ARGS)
+{
+ struct mdoc_node *n;
+ char *datestr;
+
+ if (mdoc->meta.date)
+ free(mdoc->meta.date);
+
+ n = mdoc->last;
+ if (NULL == n->child || '\0' == n->child->string[0]) {
+ mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
+ mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
+ return(1);
+ }
+
+ datestr = NULL;
+ mdoc_deroff(&datestr, n);
+ if (mdoc->quick)
+ mdoc->meta.date = datestr;
+ else {
+ mdoc->meta.date = mandoc_normdate(mdoc->parse,
+ datestr, n->line, n->pos);
+ free(datestr);
+ }
+ return(1);
+}
+
+static int
+post_dt(POST_ARGS)
+{
+ struct mdoc_node *nn, *n;
+ const char *cp;
+ char *p;
+
+ n = mdoc->last;
+
+ if (mdoc->meta.title)
+ free(mdoc->meta.title);
+ if (mdoc->meta.vol)
+ free(mdoc->meta.vol);
+ if (mdoc->meta.arch)
+ free(mdoc->meta.arch);
+
+ mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
+
+ /* First check that all characters are uppercase. */
+
+ if (NULL != (nn = n->child))
+ for (p = nn->string; *p; p++) {
+ if (toupper((unsigned char)*p) == *p)
+ continue;
+ mandoc_msg(MANDOCERR_TITLE_CASE,
+ mdoc->parse, nn->line,
+ nn->pos + (p - nn->string),
+ nn->string);
+ break;
+ }
+
+ /* Handles: `.Dt'
+ * title = unknown, volume = local, msec = 0, arch = NULL
+ */
+
+ if (NULL == (nn = n->child)) {
+ /* XXX: make these macro values. */
+ /* FIXME: warn about missing values. */
+ mdoc->meta.title = mandoc_strdup("UNKNOWN");
+ mdoc->meta.vol = mandoc_strdup("LOCAL");
+ mdoc->meta.msec = mandoc_strdup("1");
+ return(1);
+ }
+
+ /* Handles: `.Dt TITLE'
+ * title = TITLE, volume = local, msec = 0, arch = NULL
+ */
+
+ mdoc->meta.title = mandoc_strdup(
+ '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
+
+ if (NULL == (nn = nn->next)) {
+ /* FIXME: warn about missing msec. */
+ /* XXX: make this a macro value. */
+ mdoc->meta.vol = mandoc_strdup("LOCAL");
+ mdoc->meta.msec = mandoc_strdup("1");
+ return(1);
+ }
+
+ /* Handles: `.Dt TITLE SEC'
+ * title = TITLE,
+ * volume = SEC is msec ? format(msec) : SEC,
+ * msec = SEC is msec ? atoi(msec) : 0,
+ * arch = NULL
+ */
+
+ cp = mandoc_a2msec(nn->string);
+ if (cp) {
+ mdoc->meta.vol = mandoc_strdup(cp);
+ mdoc->meta.msec = mandoc_strdup(nn->string);
+ } else {
+ mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
+ nn->line, nn->pos, nn->string);
+ mdoc->meta.vol = mandoc_strdup(nn->string);
+ mdoc->meta.msec = mandoc_strdup(nn->string);
+ }
+
+ if (NULL == (nn = nn->next))
+ return(1);
+
+ /* Handles: `.Dt TITLE SEC VOL'
+ * title = TITLE,
+ * volume = VOL is vol ? format(VOL) :
+ * VOL is arch ? format(arch) :
+ * VOL
+ */
+
+ cp = mdoc_a2vol(nn->string);
+ if (cp) {
+ free(mdoc->meta.vol);
+ mdoc->meta.vol = mandoc_strdup(cp);
+ } else {
+ cp = mdoc_a2arch(nn->string);
+ if (NULL == cp) {
+ mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
+ nn->line, nn->pos, nn->string);
+ free(mdoc->meta.vol);
+ mdoc->meta.vol = mandoc_strdup(nn->string);
+ } else
+ mdoc->meta.arch = mandoc_strdup(cp);
+ }
+
+ /* Ignore any subsequent parameters... */
+ /* FIXME: warn about subsequent parameters. */
+