]> git.cameronkatri.com Git - mandoc.git/blobdiff - mdoc_validate.c
Use the default width for .RS without arguments.
[mandoc.git] / mdoc_validate.c
index c87ab23ff1add21eccf9bc244082f7be22ce629c..d090ff7ac5f60ac339b4a8396e5a53310fea04db 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: mdoc_validate.c,v 1.277 2015/02/10 08:05:30 schwarze Exp $ */
+/*     $Id: mdoc_validate.c,v 1.285 2015/04/02 22:48:17 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -8,9 +8,9 @@
  * 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
+ * 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 AUTHOR BE LIABLE FOR
+ * 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
 #include <string.h>
 #include <time.h>
 
-#include "mdoc.h"
-#include "mandoc.h"
 #include "mandoc_aux.h"
-#include "libmdoc.h"
+#include "mandoc.h"
+#include "roff.h"
+#include "mdoc.h"
 #include "libmandoc.h"
+#include "libmdoc.h"
 
 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
 
-#define        PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
+#define        PRE_ARGS  struct mdoc *mdoc, struct roff_node *n
 #define        POST_ARGS struct mdoc *mdoc
 
 enum   check_ineq {
@@ -58,11 +59,11 @@ struct      valids {
 
 static void     check_text(struct mdoc *, int, int, char *);
 static void     check_argv(struct mdoc *,
-                       struct mdoc_node *, struct mdoc_argv *);
-static void     check_args(struct mdoc *, struct mdoc_node *);
-static int      child_an(const struct mdoc_node *);
-static enum mdoc_sec   a2sec(const char *);
-static size_t          macro2len(enum mdoct);
+                       struct roff_node *, struct mdoc_argv *);
+static void     check_args(struct mdoc *, struct roff_node *);
+static int      child_an(const struct roff_node *);
+static enum roff_sec   a2sec(const char *);
+static size_t          macro2len(int);
 static void     rewrite_macro2len(char **);
 
 static void     post_an(POST_ARGS);
@@ -247,7 +248,7 @@ static      const struct valids mdoc_valids[MDOC_MAX] = {
 
 #define        RSORD_MAX 14 /* Number of `Rs' blocks. */
 
-static const enum mdoct rsord[RSORD_MAX] = {
+static const int rsord[RSORD_MAX] = {
        MDOC__A,
        MDOC__T,
        MDOC__B,
@@ -292,19 +293,20 @@ static    const char * const secnames[SEC__MAX] = {
 
 
 void
-mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
+mdoc_valid_pre(struct mdoc *mdoc, struct roff_node *n)
 {
        v_pre    p;
 
        switch (n->type) {
-       case MDOC_TEXT:
-               check_text(mdoc, n->line, n->pos, n->string);
+       case ROFFT_TEXT:
+               if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd)
+                       check_text(mdoc, n->line, n->pos, n->string);
                /* FALLTHROUGH */
-       case MDOC_TBL:
+       case ROFFT_TBL:
                /* FALLTHROUGH */
-       case MDOC_EQN:
+       case ROFFT_EQN:
                /* FALLTHROUGH */
-       case MDOC_ROOT:
+       case ROFFT_ROOT:
                return;
        default:
                break;
@@ -319,22 +321,22 @@ mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
 void
 mdoc_valid_post(struct mdoc *mdoc)
 {
-       struct mdoc_node *n;
+       struct roff_node *n;
        v_post p;
 
        n = mdoc->last;
        if (n->flags & MDOC_VALID)
                return;
-       n->flags |= MDOC_VALID;
+       n->flags |= MDOC_VALID | MDOC_ENDED;
 
        switch (n->type) {
-       case MDOC_TEXT:
+       case ROFFT_TEXT:
                /* FALLTHROUGH */
-       case MDOC_EQN:
+       case ROFFT_EQN:
                /* FALLTHROUGH */
-       case MDOC_TBL:
+       case ROFFT_TBL:
                break;
-       case MDOC_ROOT:
+       case ROFFT_ROOT:
                post_root(mdoc);
                break;
        default:
@@ -360,7 +362,7 @@ mdoc_valid_post(struct mdoc *mdoc)
 }
 
 static void
-check_args(struct mdoc *mdoc, struct mdoc_node *n)
+check_args(struct mdoc *mdoc, struct roff_node *n)
 {
        int              i;
 
@@ -373,7 +375,7 @@ check_args(struct mdoc *mdoc, struct mdoc_node *n)
 }
 
 static void
-check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
+check_argv(struct mdoc *mdoc, struct roff_node *n, struct mdoc_argv *v)
 {
        int              i;
 
@@ -397,13 +399,13 @@ check_text(struct mdoc *mdoc, int ln, int pos, char *p)
 static void
 pre_display(PRE_ARGS)
 {
-       struct mdoc_node *node;
+       struct roff_node *node;
 
-       if (MDOC_BLOCK != n->type)
+       if (n->type != ROFFT_BLOCK)
                return;
 
        for (node = mdoc->last->parent; node; node = node->parent)
-               if (MDOC_BLOCK == node->type)
+               if (node->type == ROFFT_BLOCK)
                        if (MDOC_Bd == node->tok)
                                break;
 
@@ -416,24 +418,13 @@ pre_display(PRE_ARGS)
 static void
 pre_bl(PRE_ARGS)
 {
-       struct mdoc_node *np;
        struct mdoc_argv *argv, *wa;
        int               i;
        enum mdocargt     mdoclt;
        enum mdoc_list    lt;
 
-       if (MDOC_BLOCK != n->type) {
-               if (ENDBODY_NOT != n->end) {
-                       assert(n->pending);
-                       np = n->pending->parent;
-               } else
-                       np = n->parent;
-
-               assert(np);
-               assert(MDOC_BLOCK == np->type);
-               assert(MDOC_Bl == np->tok);
+       if (n->type != ROFFT_BLOCK)
                return;
-       }
 
        /*
         * First figure out which kind of list to use: bind ourselves to
@@ -609,25 +600,14 @@ pre_bl(PRE_ARGS)
 static void
 pre_bd(PRE_ARGS)
 {
-       struct mdoc_node *np;
        struct mdoc_argv *argv;
        int               i;
        enum mdoc_disp    dt;
 
        pre_literal(mdoc, n);
 
-       if (MDOC_BLOCK != n->type) {
-               if (ENDBODY_NOT != n->end) {
-                       assert(n->pending);
-                       np = n->pending->parent;
-               } else
-                       np = n->parent;
-
-               assert(np);
-               assert(MDOC_BLOCK == np->type);
-               assert(MDOC_Bd == np->tok);
+       if (n->type != ROFFT_BLOCK)
                return;
-       }
 
        for (i = 0; n->args && i < (int)n->args->argc; i++) {
                argv = n->args->argv + i;
@@ -739,7 +719,7 @@ static void
 pre_obsolete(PRE_ARGS)
 {
 
-       if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
+       if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
                mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
                    n->line, n->pos, mdoc_macronames[n->tok]);
 }
@@ -789,7 +769,7 @@ pre_dd(PRE_ARGS)
 static void
 post_bf(POST_ARGS)
 {
-       struct mdoc_node *np, *nch;
+       struct roff_node *np, *nch;
        enum mdocargt     arg;
 
        /*
@@ -797,23 +777,11 @@ post_bf(POST_ARGS)
         * element, which contains the goods.
         */
 
-       if (MDOC_HEAD != mdoc->last->type) {
-               if (ENDBODY_NOT != mdoc->last->end) {
-                       assert(mdoc->last->pending);
-                       np = mdoc->last->pending->parent->head;
-               } else if (MDOC_BLOCK != mdoc->last->type) {
-                       np = mdoc->last->parent->head;
-               } else
-                       np = mdoc->last->head;
-
-               assert(np);
-               assert(MDOC_HEAD == np->type);
-               assert(MDOC_Bf == np->tok);
+       np = mdoc->last;
+       if (np->type != ROFFT_HEAD)
                return;
-       }
 
-       np = mdoc->last;
-       assert(MDOC_BLOCK == np->parent->type);
+       assert(np->parent->type == ROFFT_BLOCK);
        assert(MDOC_Bf == np->parent->tok);
 
        /* Check the number of arguments. */
@@ -863,16 +831,16 @@ post_bf(POST_ARGS)
 static void
 post_lb(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
        const char              *stdlibname;
        char                    *libname;
 
        n = mdoc->last->child;
-       assert(MDOC_TEXT == n->type);
+       assert(n->type == ROFFT_TEXT);
 
        if (NULL == (stdlibname = mdoc_a2lib(n->string)))
                mandoc_asprintf(&libname,
-                   "library \\(lq%s\\(rq", n->string);
+                   "library \\(Lq%s\\(Rq", n->string);
        else
                libname = mandoc_strdup(stdlibname);
 
@@ -883,7 +851,7 @@ post_lb(POST_ARGS)
 static void
 post_eoln(POST_ARGS)
 {
-       const struct mdoc_node *n;
+       const struct roff_node *n;
 
        n = mdoc->last;
        if (n->child)
@@ -896,7 +864,7 @@ post_eoln(POST_ARGS)
 static void
 post_fname(POST_ARGS)
 {
-       const struct mdoc_node  *n;
+       const struct roff_node  *n;
        const char              *cp;
        size_t                   pos;
 
@@ -919,11 +887,11 @@ post_fn(POST_ARGS)
 static void
 post_fo(POST_ARGS)
 {
-       const struct mdoc_node  *n;
+       const struct roff_node  *n;
 
        n = mdoc->last;
 
-       if (n->type != MDOC_HEAD)
+       if (n->type != ROFFT_HEAD)
                return;
 
        if (n->child == NULL) {
@@ -945,7 +913,7 @@ post_fo(POST_ARGS)
 static void
 post_fa(POST_ARGS)
 {
-       const struct mdoc_node *n;
+       const struct roff_node *n;
        const char *cp;
 
        for (n = mdoc->last->child; n != NULL; n = n->next) {
@@ -966,7 +934,7 @@ post_fa(POST_ARGS)
 static void
 post_vt(POST_ARGS)
 {
-       const struct mdoc_node *n;
+       const struct roff_node *n;
 
        /*
         * The Vt macro comes in both ELEM and BLOCK form, both of which
@@ -976,11 +944,11 @@ post_vt(POST_ARGS)
         * specifically the BODY, should only have TEXT children.
         */
 
-       if (MDOC_BODY != mdoc->last->type)
+       if (mdoc->last->type != ROFFT_BODY)
                return;
 
        for (n = mdoc->last->child; n; n = n->next)
-               if (MDOC_TEXT != n->type)
+               if (n->type != ROFFT_TEXT)
                        mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
                            n->line, n->pos, mdoc_macronames[n->tok]);
 }
@@ -988,7 +956,7 @@ post_vt(POST_ARGS)
 static void
 post_nm(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
 
        n = mdoc->last;
 
@@ -1010,11 +978,11 @@ post_nm(POST_ARGS)
 static void
 post_nd(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
 
        n = mdoc->last;
 
-       if (n->type != MDOC_BODY)
+       if (n->type != ROFFT_BODY)
                return;
 
        if (n->child == NULL)
@@ -1027,11 +995,11 @@ post_nd(POST_ARGS)
 static void
 post_d1(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
 
        n = mdoc->last;
 
-       if (n->type != MDOC_BODY)
+       if (n->type != ROFFT_BODY)
                return;
 
        if (n->child == NULL)
@@ -1044,11 +1012,11 @@ post_d1(POST_ARGS)
 static void
 post_literal(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
 
        n = mdoc->last;
 
-       if (n->type != MDOC_BODY)
+       if (n->type != ROFFT_BODY)
                return;
 
        if (n->child == NULL)
@@ -1066,7 +1034,7 @@ post_literal(POST_ARGS)
 static void
 post_defaults(POST_ARGS)
 {
-       struct mdoc_node *nn;
+       struct roff_node *nn;
 
        /*
         * The `Ar' defaults to "file ..." if no value is provided as an
@@ -1100,7 +1068,7 @@ post_defaults(POST_ARGS)
 static void
 post_at(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
        const char              *std_att;
        char                    *att;
 
@@ -1119,7 +1087,7 @@ post_at(POST_ARGS)
         */
 
        n = n->child;
-       assert(MDOC_TEXT == n->type);
+       assert(n->type == ROFFT_TEXT);
        if (NULL == (std_att = mdoc_a2att(n->string))) {
                mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
                    n->line, n->pos, "At %s", n->string);
@@ -1134,7 +1102,7 @@ post_at(POST_ARGS)
 static void
 post_an(POST_ARGS)
 {
-       struct mdoc_node *np, *nch;
+       struct roff_node *np, *nch;
 
        np = mdoc->last;
        nch = np->child;
@@ -1151,7 +1119,7 @@ static void
 post_en(POST_ARGS)
 {
 
-       if (MDOC_BLOCK == mdoc->last->type)
+       if (mdoc->last->type == ROFFT_BLOCK)
                mdoc->last->norm->Es = mdoc->last_es;
 }
 
@@ -1165,12 +1133,12 @@ post_es(POST_ARGS)
 static void
 post_it(POST_ARGS)
 {
+       struct roff_node *nbl, *nit, *nch;
        int               i, cols;
        enum mdoc_list    lt;
-       struct mdoc_node *nbl, *nit, *nch;
 
        nit = mdoc->last;
-       if (nit->type != MDOC_BLOCK)
+       if (nit->type != ROFFT_BLOCK)
                return;
 
        nbl = nit->parent->parent;
@@ -1217,7 +1185,7 @@ post_it(POST_ARGS)
                assert(nit->head->child == NULL);
 
                for (i = 0, nch = nit->child; nch; nch = nch->next)
-                       if (nch->type == MDOC_BODY)
+                       if (nch->type == ROFFT_BODY)
                                i++;
 
                if (i < cols || i > cols + 1)
@@ -1233,7 +1201,7 @@ post_it(POST_ARGS)
 static void
 post_bl_block(POST_ARGS)
 {
-       struct mdoc_node *n, *ni, *nc;
+       struct roff_node *n, *ni, *nc;
 
        /*
         * These are fairly complicated, so we've broken them into two
@@ -1294,7 +1262,7 @@ void
 rewrite_macro2len(char **arg)
 {
        size_t            width;
-       enum mdoct        tok;
+       int               tok;
 
        if (*arg == NULL)
                return;
@@ -1312,7 +1280,7 @@ rewrite_macro2len(char **arg)
 static void
 post_bl_block_tag(POST_ARGS)
 {
-       struct mdoc_node *n, *nn;
+       struct roff_node *n, *nn;
        size_t            sz, ssz;
        int               i;
        char              buf[24];
@@ -1331,13 +1299,13 @@ post_bl_block_tag(POST_ARGS)
                if (MDOC_It != nn->tok)
                        continue;
 
-               assert(MDOC_BLOCK == nn->type);
+               assert(nn->type == ROFFT_BLOCK);
                nn = nn->head->child;
 
                if (nn == NULL)
                        break;
 
-               if (MDOC_TEXT == nn->type) {
+               if (nn->type == ROFFT_TEXT) {
                        sz = strlen(nn->string) + 1;
                        break;
                }
@@ -1377,7 +1345,7 @@ post_bl_block_tag(POST_ARGS)
 static void
 post_bl_head(POST_ARGS)
 {
-       struct mdoc_node *nbl, *nh, *nch, *nnext;
+       struct roff_node *nbl, *nh, *nch, *nnext;
        struct mdoc_argv *argv;
        int               i, j;
 
@@ -1439,19 +1407,19 @@ post_bl_head(POST_ARGS)
 static void
 post_bl(POST_ARGS)
 {
-       struct mdoc_node        *nparent, *nprev; /* of the Bl block */
-       struct mdoc_node        *nblock, *nbody;  /* of the Bl */
-       struct mdoc_node        *nchild, *nnext;  /* of the Bl body */
+       struct roff_node        *nparent, *nprev; /* of the Bl block */
+       struct roff_node        *nblock, *nbody;  /* of the Bl */
+       struct roff_node        *nchild, *nnext;  /* of the Bl body */
 
        nbody = mdoc->last;
        switch (nbody->type) {
-       case MDOC_BLOCK:
+       case ROFFT_BLOCK:
                post_bl_block(mdoc);
                return;
-       case MDOC_HEAD:
+       case ROFFT_HEAD:
                post_bl_head(mdoc);
                return;
-       case MDOC_BODY:
+       case ROFFT_BODY:
                break;
        default:
                return;
@@ -1522,11 +1490,11 @@ post_bl(POST_ARGS)
 static void
 post_bk(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
 
        n = mdoc->last;
 
-       if (n->type == MDOC_BLOCK && n->body->child == NULL) {
+       if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
                mandoc_msg(MANDOCERR_BLK_EMPTY,
                    mdoc->parse, n->line, n->pos, "Bk");
                mdoc_node_delete(mdoc, n);
@@ -1536,7 +1504,7 @@ post_bk(POST_ARGS)
 static void
 post_sm(struct mdoc *mdoc)
 {
-       struct mdoc_node        *nch;
+       struct roff_node        *nch;
 
        nch = mdoc->last->child;
 
@@ -1545,7 +1513,7 @@ post_sm(struct mdoc *mdoc)
                return;
        }
 
-       assert(nch->type == MDOC_TEXT);
+       assert(nch->type == ROFFT_TEXT);
 
        if ( ! strcmp(nch->string, "on")) {
                mdoc->flags &= ~MDOC_SMOFF;
@@ -1566,7 +1534,7 @@ post_sm(struct mdoc *mdoc)
 static void
 post_root(POST_ARGS)
 {
-       struct mdoc_node *n;
+       struct roff_node *n;
 
        /* Add missing prologue data. */
 
@@ -1606,13 +1574,13 @@ post_root(POST_ARGS)
 static void
 post_st(POST_ARGS)
 {
-       struct mdoc_node         *n, *nch;
+       struct roff_node         *n, *nch;
        const char               *p;
 
        n = mdoc->last;
        nch = n->child;
 
-       assert(MDOC_TEXT == nch->type);
+       assert(nch->type == ROFFT_TEXT);
 
        if (NULL == (p = mdoc_a2st(nch->string))) {
                mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
@@ -1627,12 +1595,12 @@ post_st(POST_ARGS)
 static void
 post_rs(POST_ARGS)
 {
-       struct mdoc_node *np, *nch, *next, *prev;
+       struct roff_node *np, *nch, *next, *prev;
        int               i, j;
 
        np = mdoc->last;
 
-       if (np->type != MDOC_BODY)
+       if (np->type != ROFFT_BODY)
                return;
 
        if (np->child == NULL) {
@@ -1721,11 +1689,11 @@ post_rs(POST_ARGS)
 static void
 post_hyph(POST_ARGS)
 {
-       struct mdoc_node        *nch;
+       struct roff_node        *nch;
        char                    *cp;
 
        for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
-               if (nch->type != MDOC_TEXT)
+               if (nch->type != ROFFT_TEXT)
                        continue;
                cp = nch->string;
                if (*cp == '\0')
@@ -1754,10 +1722,10 @@ post_sh(POST_ARGS)
        post_ignpar(mdoc);
 
        switch (mdoc->last->type) {
-       case MDOC_HEAD:
+       case ROFFT_HEAD:
                post_sh_head(mdoc);
                break;
-       case MDOC_BODY:
+       case ROFFT_BODY:
                switch (mdoc->lastsec)  {
                case SEC_NAME:
                        post_sh_name(mdoc);
@@ -1780,41 +1748,45 @@ post_sh(POST_ARGS)
 static void
 post_sh_name(POST_ARGS)
 {
-       struct mdoc_node *n;
+       struct roff_node *n;
+       int hasnm, hasnd;
 
-       /*
-        * Warn if the NAME section doesn't contain the `Nm' and `Nd'
-        * macros (can have multiple `Nm' and one `Nd').  Note that the
-        * children of the BODY declaration can also be "text".
-        */
-
-       if (NULL == (n = mdoc->last->child)) {
-               mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
-                   mdoc->last->line, mdoc->last->pos, "empty");
-               return;
-       }
+       hasnm = hasnd = 0;
 
-       for ( ; n && n->next; n = n->next) {
-               if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
-                       continue;
-               if (MDOC_TEXT == n->type)
-                       continue;
-               mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
-                   n->line, n->pos, mdoc_macronames[n->tok]);
+       for (n = mdoc->last->child; n != NULL; n = n->next) {
+               switch (n->tok) {
+               case MDOC_Nm:
+                       hasnm = 1;
+                       break;
+               case MDOC_Nd:
+                       hasnd = 1;
+                       if (n->next != NULL)
+                               mandoc_msg(MANDOCERR_NAMESEC_ND,
+                                   mdoc->parse, n->line, n->pos, NULL);
+                       break;
+               case MDOC_MAX:
+                       if (hasnm)
+                               break;
+                       /* FALLTHROUGH */
+               default:
+                       mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
+                           n->line, n->pos, mdoc_macronames[n->tok]);
+                       break;
+               }
        }
 
-       assert(n);
-       if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
-               return;
-
-       mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
-           n->line, n->pos, mdoc_macronames[n->tok]);
+       if ( ! hasnm)
+               mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
+                   mdoc->last->line, mdoc->last->pos, NULL);
+       if ( ! hasnd)
+               mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
+                   mdoc->last->line, mdoc->last->pos, NULL);
 }
 
 static void
 post_sh_see_also(POST_ARGS)
 {
-       const struct mdoc_node  *n;
+       const struct roff_node  *n;
        const char              *name, *sec;
        const char              *lastname, *lastsec, *lastpunct;
        int                      cmp;
@@ -1859,7 +1831,7 @@ post_sh_see_also(POST_ARGS)
                        lastpunct = "none";
                        continue;
                }
-               if (n->type != MDOC_TEXT)
+               if (n->type != ROFFT_TEXT)
                        break;
                for (name = n->string; *name != '\0'; name++)
                        if (isalpha((const unsigned char)*name))
@@ -1874,7 +1846,7 @@ post_sh_see_also(POST_ARGS)
 }
 
 static int
-child_an(const struct mdoc_node *n)
+child_an(const struct roff_node *n)
 {
 
        for (n = n->child; n != NULL; n = n->next)
@@ -1895,10 +1867,10 @@ post_sh_authors(POST_ARGS)
 static void
 post_sh_head(POST_ARGS)
 {
-       struct mdoc_node *n;
+       struct roff_node *n;
        const char      *goodsec;
        char            *secname;
-       enum mdoc_sec    sec;
+       enum roff_sec    sec;
 
        /*
         * Process a new section.  Sections are either "named" or
@@ -2015,13 +1987,13 @@ post_sh_head(POST_ARGS)
 static void
 post_ignpar(POST_ARGS)
 {
-       struct mdoc_node *np;
+       struct roff_node *np;
 
        switch (mdoc->last->type) {
-       case MDOC_HEAD:
+       case ROFFT_HEAD:
                post_hyph(mdoc);
                return;
-       case MDOC_BODY:
+       case ROFFT_BODY:
                break;
        default:
                return;
@@ -2052,7 +2024,7 @@ pre_par(PRE_ARGS)
 
        if (NULL == mdoc->last)
                return;
-       if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
+       if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
                return;
 
        /*
@@ -2081,7 +2053,7 @@ pre_par(PRE_ARGS)
 static void
 post_par(POST_ARGS)
 {
-       struct mdoc_node *np;
+       struct roff_node *np;
 
        np = mdoc->last;
 
@@ -2117,7 +2089,7 @@ pre_literal(PRE_ARGS)
 
        pre_display(mdoc, n);
 
-       if (MDOC_BODY != n->type)
+       if (n->type != ROFFT_BODY)
                return;
 
        /*
@@ -2144,7 +2116,7 @@ pre_literal(PRE_ARGS)
 static void
 post_dd(POST_ARGS)
 {
-       struct mdoc_node *n;
+       struct roff_node *n;
        char             *datestr;
 
        if (mdoc->meta.date)
@@ -2173,7 +2145,7 @@ out:
 static void
 post_dt(POST_ARGS)
 {
-       struct mdoc_node *nn, *n;
+       struct roff_node *nn, *n;
        const char       *cp;
        char             *p;
 
@@ -2189,70 +2161,68 @@ post_dt(POST_ARGS)
        mdoc->meta.vol = NULL;
        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_vmsg(MANDOCERR_TITLE_CASE,
-                           mdoc->parse, nn->line,
-                           nn->pos + (p - nn->string),
-                           "Dt %s", nn->string);
-                       break;
-               }
-
-       /* No argument: msec and arch remain NULL. */
+       /* Mandatory first argument: title. */
 
-       if (NULL == (nn = n->child)) {
+       nn = n->child;
+       if (nn == NULL || *nn->string == '\0') {
                mandoc_msg(MANDOCERR_DT_NOTITLE,
                    mdoc->parse, n->line, n->pos, "Dt");
                mdoc->meta.title = mandoc_strdup("UNTITLED");
-               mdoc->meta.vol = mandoc_strdup("LOCAL");
-               goto out;
+       } else {
+               mdoc->meta.title = mandoc_strdup(nn->string);
+
+               /* Check that all characters are uppercase. */
+
+               for (p = nn->string; *p != '\0'; p++)
+                       if (islower((unsigned char)*p)) {
+                               mandoc_vmsg(MANDOCERR_TITLE_CASE,
+                                   mdoc->parse, nn->line,
+                                   nn->pos + (p - nn->string),
+                                   "Dt %s", nn->string);
+                               break;
+                       }
        }
 
-       /* One argument: msec and arch remain NULL. */
+       /* Mandatory second argument: section. */
 
-       mdoc->meta.title = mandoc_strdup(
-           '\0' == nn->string[0] ? "UNTITLED" : nn->string);
+       if (nn != NULL)
+               nn = nn->next;
 
-       if (NULL == (nn = nn->next)) {
+       if (nn == NULL) {
                mandoc_vmsg(MANDOCERR_MSEC_MISSING,
                    mdoc->parse, n->line, n->pos,
                    "Dt %s", mdoc->meta.title);
                mdoc->meta.vol = mandoc_strdup("LOCAL");
-               goto out;
+               goto out;  /* msec and arch remain NULL. */
        }
 
-       /* Handles: `.Dt TITLE SEC'
-        * title = TITLE,
-        * volume = SEC is msec ? format(msec) : SEC,
-        * msec = SEC is msec ? atoi(msec) : 0,
-        * arch = NULL
-        */
+       mdoc->meta.msec = mandoc_strdup(nn->string);
+
+       /* Infer volume title from section number. */
 
        cp = mandoc_a2msec(nn->string);
-       if (cp) {
-               mdoc->meta.vol = mandoc_strdup(cp);
-               mdoc->meta.msec = mandoc_strdup(nn->string);
-       } else {
+       if (cp == NULL) {
                mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
                    nn->line, nn->pos, "Dt ... %s", nn->string);
                mdoc->meta.vol = mandoc_strdup(nn->string);
-               mdoc->meta.msec = mandoc_strdup(nn->string);
-       }
+       } else
+               mdoc->meta.vol = mandoc_strdup(cp);
 
-       /* Handle an optional architecture */
+       /* Optional third argument: architecture. */
 
-       if ((nn = nn->next) != NULL) {
-               for (p = nn->string; *p; p++)
-                       *p = tolower((unsigned char)*p);
-               mdoc->meta.arch = mandoc_strdup(nn->string);
-       }
+       if ((nn = nn->next) == NULL)
+               goto out;
+
+       for (p = nn->string; *p != '\0'; p++)
+               *p = tolower((unsigned char)*p);
+       mdoc->meta.arch = mandoc_strdup(nn->string);
+
+       /* Ignore fourth and later arguments. */
+
+       if ((nn = nn->next) != NULL)
+               mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
+                   nn->line, nn->pos, "Dt ... %s", nn->string);
 
-       /* Ignore any subsequent parameters... */
-       /* FIXME: warn about subsequent parameters. */
 out:
        mdoc_node_delete(mdoc, n);
 }
@@ -2260,7 +2230,7 @@ out:
 static void
 post_bx(POST_ARGS)
 {
-       struct mdoc_node        *n;
+       struct roff_node        *n;
 
        /*
         * Make `Bx's second argument always start with an uppercase
@@ -2280,7 +2250,7 @@ post_os(POST_ARGS)
        struct utsname    utsname;
        static char      *defbuf;
 #endif
-       struct mdoc_node *n;
+       struct roff_node *n;
 
        n = mdoc->last;
 
@@ -2330,7 +2300,7 @@ out:
 static void
 post_ex(POST_ARGS)
 {
-       struct mdoc_node *n;
+       struct roff_node *n;
 
        n = mdoc->last;
 
@@ -2348,20 +2318,20 @@ post_ex(POST_ARGS)
        mdoc->last = n;
 }
 
-static enum mdoc_sec
+static enum roff_sec
 a2sec(const char *p)
 {
        int              i;
 
        for (i = 0; i < (int)SEC__MAX; i++)
                if (secnames[i] && 0 == strcmp(p, secnames[i]))
-                       return((enum mdoc_sec)i);
+                       return((enum roff_sec)i);
 
        return(SEC_CUSTOM);
 }
 
 static size_t
-macro2len(enum mdoct macro)
+macro2len(int macro)
 {
 
        switch (macro) {