]> git.cameronkatri.com Git - mandoc.git/blobdiff - roff.c
*** empty log message ***
[mandoc.git] / roff.c
diff --git a/roff.c b/roff.c
index 4870e9934e877abc0a4d6905a37282ee5fabebf3..37d62c041c77e01e667d92a4489339fe7901e903 100644 (file)
--- a/roff.c
+++ b/roff.c
@@ -1,4 +1,4 @@
-/* $Id: roff.c,v 1.40 2008/12/04 23:10:51 kristaps Exp $ */
+/* $Id: roff.c,v 1.49 2008/12/07 16:41:04 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -30,6 +30,7 @@
 
 #include "libmdocml.h"
 #include "private.h"
+#include "roff.h"
 
 /* FIXME: First letters of quoted-text interpreted in rofffindtok. */
 /* FIXME: `No' not implemented. */
 /* TODO: unify empty-content tags a la <br />. */
 /* TODO: macros with a set number of arguments? */
 /* TODO: validate Dt macro arguments. */
-
-enum   roffd { 
-       ROFF_ENTER = 0, 
-       ROFF_EXIT 
-};
-
-enum   rofftype { 
-       ROFF_COMMENT, 
-       ROFF_TEXT, 
-       ROFF_LAYOUT,
-       ROFF_SPECIAL
-};
-
-#define        ROFFCALL_ARGS \
-       int tok, struct rofftree *tree, \
-       char *argv[], enum roffd type
-
-struct rofftree;
-
-struct rofftok {
-       int             (*cb)(ROFFCALL_ARGS);   /* Callback. */
-       const int        *args;                 /* Args (or NULL). */
-       const int        *parents;              /* Limit to parents. */
-       const int        *children;             /* Limit to kids. */
-       int               ctx;                  /* Blk-close node. */
-       enum rofftype     type;                 /* Type of macro. */
-       int               flags;
-#define        ROFF_PARSED      (1 << 0)               /* "Parsed". */
-#define        ROFF_CALLABLE    (1 << 1)               /* "Callable". */
-#define        ROFF_SHALLOW     (1 << 2)               /* Nesting block. */
-#define        ROFF_LSCOPE      (1 << 3)               /* Line scope. */
-};
+/* FIXME: Bl -diag supposed to ignore callable children. */
+/* FIXME: Nm has newline when used in NAME section. */
 
 struct roffnode {
        int               tok;                  /* Token id. */
@@ -83,6 +54,7 @@ struct        rofftree {
        struct roffnode  *last;                 /* Last parsed node. */
        char             *cur;                  /* Line start. */
        struct tm         tm;                   /* `Dd' results. */
+       char              name[64];             /* `Nm' results. */
        char              os[64];               /* `Os' results. */
        char              title[64];            /* `Dt' results. */
        char              section[64];          /* `Dt' results. */
@@ -97,16 +69,6 @@ struct       rofftree {
        void             *arg;                  /* Callbacks' arg. */
 };
 
-static int               roff_Dd(ROFFCALL_ARGS);
-static int               roff_Dt(ROFFCALL_ARGS);
-static int               roff_Os(ROFFCALL_ARGS);
-static int               roff_Ns(ROFFCALL_ARGS);
-static int               roff_Sm(ROFFCALL_ARGS);
-static int               roff_layout(ROFFCALL_ARGS);
-static int               roff_text(ROFFCALL_ARGS);
-static int               roff_noop(ROFFCALL_ARGS);
-static int               roff_depr(ROFFCALL_ARGS);
-static int               roff_ordered(ROFFCALL_ARGS);
 static struct roffnode  *roffnode_new(int, struct rofftree *);
 static void              roffnode_free(struct rofftree *);
 static void              roff_warn(const struct rofftree *, 
@@ -129,7 +91,10 @@ static      int               roffcall(struct rofftree *, int, char **);
 static int               roffparse(struct rofftree *, char *);
 static int               textparse(struct rofftree *, char *);
 static int               roffdata(struct rofftree *, int, char *);
-static int               roffspecial(struct rofftree *, int, char **);
+static int               roffspecial(struct rofftree *, int, 
+                               const char *, const int *,
+                               const char **, size_t, char **);
+static int               roffsetname(struct rofftree *, char **);
 
 #ifdef __linux__ 
 extern size_t            strlcat(char *, const char *, size_t);
@@ -140,228 +105,6 @@ extern    char             *strptime(const char *, const char *,
                                struct tm *);
 #endif
 
-
-static const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit, ROFF_ARGMAX };
-static const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled, ROFF_Literal,
-       ROFF_File, ROFF_Offset, ROFF_Filled, ROFF_Compact, ROFF_ARGMAX };
-static const int roffarg_Bk[] = { ROFF_Words, ROFF_ARGMAX };
-static const int roffarg_Ex[] = { ROFF_Std, ROFF_ARGMAX };
-static const int roffarg_Rv[] = { ROFF_Std, ROFF_ARGMAX };
-static         const int roffarg_Bl[] = { ROFF_Bullet, ROFF_Dash, ROFF_Hyphen,
-       ROFF_Item, ROFF_Enum, ROFF_Tag, ROFF_Diag, ROFF_Hang, ROFF_Ohang,
-       ROFF_Inset, ROFF_Column, ROFF_Offset, ROFF_Width, ROFF_Compact,
-       ROFF_ARGMAX };
-static         const int roffarg_St[] = { ROFF_p1003_1_88, ROFF_p1003_1_90,
-       ROFF_p1003_1_96, ROFF_p1003_1_2001, ROFF_p1003_1_2004, ROFF_p1003_1,
-       ROFF_p1003_1b, ROFF_p1003_1b_93, ROFF_p1003_1c_95, ROFF_p1003_1g_2000,
-       ROFF_p1003_2_92, ROFF_p1387_2_95, ROFF_p1003_2, ROFF_p1387_2,
-       ROFF_isoC_90, ROFF_isoC_amd1, ROFF_isoC_tcor1, ROFF_isoC_tcor2,
-       ROFF_isoC_99, ROFF_ansiC, ROFF_ansiC_89, ROFF_ansiC_99, ROFF_ieee754,
-       ROFF_iso8802_3, ROFF_xpg3, ROFF_xpg4, ROFF_xpg4_2, ROFF_xpg4_3,
-       ROFF_xbd5, ROFF_xcu5, ROFF_xsh5, ROFF_xns5, ROFF_xns5_2d2_0,
-       ROFF_xcurses4_2, ROFF_susv2, ROFF_susv3, ROFF_svid4, ROFF_ARGMAX };
-
-static const int roffchild_Bl[] = { ROFF_It, ROFF_El, ROFF_MAX };
-static const int roffchild_Fo[] = { ROFF_Fa, ROFF_Fc, ROFF_MAX };
-static const int roffchild_Rs[] = { ROFF_Re, ROFF__A, ROFF__B, ROFF__D,
-       ROFF__I, ROFF__J, ROFF__N, ROFF__O, ROFF__P, ROFF__R, ROFF__T, ROFF__V,
-       ROFF_MAX };
-
-static const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
-static const int roffparent_Fc[] = { ROFF_Fo, ROFF_Fa, ROFF_MAX };
-static const int roffparent_Oc[] = { ROFF_Oo, ROFF_MAX };
-static const int roffparent_It[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
-static const int roffparent_Re[] = { ROFF_Rs, ROFF_MAX };
-
-static const struct rofftok tokens[ROFF_MAX] = {
-       {   roff_noop, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */
-       {     roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */
-       {     roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */
-       {     roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */
-       { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, 0 }, /* Sh */
-       { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, 0 }, /* Ss */ 
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Pp */ /* XXX 0 args */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_LSCOPE }, /* D1 */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_LSCOPE }, /* Dl */
-       { roff_layout, roffarg_Bd, NULL, NULL, 0, ROFF_LAYOUT, 0 },     /* Bd */
-       {   roff_noop, NULL, NULL, NULL, ROFF_Bd, ROFF_LAYOUT, 0 }, /* Ed */
-       { roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */
-       {   roff_noop, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */
-       { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, ROFF_PARSED | ROFF_SHALLOW }, /* It */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */ /* FIXME */
-       {   roff_text, roffarg_An, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Cd */ /* XXX man.4 only */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */ /* XXX needs arg */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */ /* XXX needs arg */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ev */ /* XXX needs arg */
-       {   roff_text, roffarg_Ex, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ex */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fa */ /* XXX needs arg */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fd */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fl */
-       {roff_ordered, NULL, NULL, NULL, 0, ROFF_TEXT, /*XXX*/ -1 }, /* Fn */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ft */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */ /* XXX needs arg */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */ /* FIXME */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Op */
-       {   roff_depr, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ot */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pa */
-       {   roff_text, roffarg_Rv, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rv */
-       {   roff_text, roffarg_St, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* St */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Va */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Vt */ /* XXX needs arg */
-       {roff_ordered, NULL, NULL, NULL, 0, ROFF_TEXT, /*XXX*/ -1 }, /* Xr */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %A */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %B */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %D */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %I */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %J */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %N */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %O */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %P */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %R */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %T */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %V */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ac */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ao */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Aq */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* At */ /* XXX at most 2 args */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bc */
-       { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bf */ /* FIXME */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bo */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Bq */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 },  /* Db */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dc */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Do */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Dq */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ec */
-       {   roff_noop, NULL, NULL, NULL, ROFF_Bf, ROFF_LAYOUT, 0 }, /* Ef */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Em */ /* XXX needs arg */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Eo */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Fx */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ms */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* No */
-       {     roff_Ns, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ns */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Nx */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ox */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pc */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Pf */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Po */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Pq */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qc */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ql */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qo */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Qq */
-       {   roff_noop, NULL, roffparent_Re, NULL, ROFF_Rs, ROFF_LAYOUT, 0 }, /* Re */
-       { roff_layout, NULL, NULL, roffchild_Rs, 0, ROFF_LAYOUT, 0 },   /* Rs */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sc */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* So */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Sq */
-       {     roff_Sm, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Sm */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sy */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */
-       {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */
-       { roff_layout, NULL, NULL, roffchild_Fo, 0, ROFF_LAYOUT, 0 }, /* Fo */
-       {   roff_noop, NULL, roffparent_Fc, NULL, ROFF_Fo, ROFF_LAYOUT, 0 }, /* Fc */
-       { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Oo */
-       {   roff_noop, NULL, roffparent_Oc, NULL, ROFF_Oo, ROFF_LAYOUT, 0 }, /* Oc */
-       { roff_layout, roffarg_Bk, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */
-       {   roff_noop, NULL, NULL, NULL, ROFF_Bk, ROFF_LAYOUT, 0 }, /* Ek */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Bt */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Hf */
-       {   roff_depr, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fr */
-       {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ud */
-       };
-
-#define        ROFF_VALUE      (1 << 0)
-
-static const int tokenargs[ROFF_ARGMAX] = {
-       0,              0,              0,              0,
-       0,              ROFF_VALUE,     ROFF_VALUE,     0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              ROFF_VALUE,     0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       0,              0,              0,              0,
-       };
-
-const  char *const toknamesp[ROFF_MAX] = {              
-       "\\\"",         "Dd",           "Dt",           "Os",
-       "Sh",           "Ss",           "Pp",           "D1",
-       "Dl",           "Bd",           "Ed",           "Bl",
-       "El",           "It",           "Ad",           "An",
-       "Ar",           "Cd",           "Cm",           "Dv",
-       "Er",           "Ev",           "Ex",           "Fa",
-       "Fd",           "Fl",           "Fn",           "Ft",
-       "Ic",           "In",           "Li",           "Nd",
-       "Nm",           "Op",           "Ot",           "Pa",
-       "Rv",           "St",           "Va",           "Vt",
-       /* LINTED */
-       "Xr",           "\%A",          "\%B",          "\%D",
-       /* LINTED */
-       "\%I",          "\%J",          "\%N",          "\%O",
-       /* LINTED */
-       "\%P",          "\%R",          "\%T",          "\%V",
-       "Ac",           "Ao",           "Aq",           "At",
-       "Bc",           "Bf",           "Bo",           "Bq",
-       "Bsx",          "Bx",           "Db",           "Dc",
-       "Do",           "Dq",           "Ec",           "Ef",
-       "Em",           "Eo",           "Fx",           "Ms",
-       "No",           "Ns",           "Nx",           "Ox",
-       "Pc",           "Pf",           "Po",           "Pq",
-       "Qc",           "Ql",           "Qo",           "Qq",
-       "Re",           "Rs",           "Sc",           "So",
-       "Sq",           "Sm",           "Sx",           "Sy",
-       "Tn",           "Ux",           "Xc",           "Xo",
-       "Fo",           "Fc",           "Oo",           "Oc",
-       "Bk",           "Ek",           "Bt",           "Hf",
-       "Fr",           "Ud",
-       };
-
-const  char *const tokargnamesp[ROFF_ARGMAX] = {                
-       "split",                "nosplit",              "ragged",
-       "unfilled",             "literal",              "file",          
-       "offset",               "bullet",               "dash",          
-       "hyphen",               "item",                 "enum",          
-       "tag",                  "diag",                 "hang",          
-       "ohang",                "inset",                "column",        
-       "width",                "compact",              "std",   
-       "p1003.1-88",           "p1003.1-90",           "p1003.1-96",
-       "p1003.1-2001",         "p1003.1-2004",         "p1003.1",
-       "p1003.1b",             "p1003.1b-93",          "p1003.1c-95",
-       "p1003.1g-2000",        "p1003.2-92",           "p1387.2-95",
-       "p1003.2",              "p1387.2",              "isoC-90",
-       "isoC-amd1",            "isoC-tcor1",           "isoC-tcor2",
-       "isoC-99",              "ansiC",                "ansiC-89",
-       "ansiC-99",             "ieee754",              "iso8802-3",
-       "xpg3",                 "xpg4",                 "xpg4.2",
-       "xpg4.3",               "xbd5",                 "xcu5",
-       "xsh5",                 "xns5",                 "xns5.2d2.0",
-       "xcurses4.2",           "susv2",                "susv3",
-       "svid4",                "filled",               "words",
-       };
-
-const  char *const *toknames = toknamesp;
-const  char *const *tokargnames = tokargnamesp;
-
-
 int
 roff_free(struct rofftree *tree, int flush)
 {
@@ -876,11 +619,88 @@ roffnode_free(struct rofftree *tree)
 
 
 static int
-roffspecial(struct rofftree *tree, int tok, char **ordp)
+roffspecial(struct rofftree *tree, int tok, const char *start, 
+               const int *argc, const char **argv, 
+               size_t sz, char **ordp)
 {
 
+       switch (tok) {
+       case (ROFF_At):
+               if (0 == sz)
+                       break;
+               if (0 == strcmp(*ordp, "v6"))
+                       break;
+               else if (0 == strcmp(*ordp, "v7")) 
+                       break;
+               else if (0 == strcmp(*ordp, "32v"))
+                       break;
+               else if (0 == strcmp(*ordp, "V.1"))
+                       break;
+               else if (0 == strcmp(*ordp, "V.4"))
+                       break;
+               roff_err(tree, start, "invalid `At' arg");
+               return(0);
+       
+       case (ROFF_Fn):
+               if (0 != sz) 
+                       break;
+               roff_err(tree, start, "`%s' expects at least "
+                               "one arg", toknames[tok]);
+               return(0);
+
+       case (ROFF_Nm):
+               if (0 == sz) {
+                       if (0 == tree->name[0]) {
+                               roff_err(tree, start, "`Nm' not set");
+                               return(0);
+                       }
+                       ordp[0] = tree->name;
+                       ordp[1] = NULL;
+               } else if ( ! roffsetname(tree, ordp))
+                       return(0);
+               break;
+
+       case (ROFF_Rv):
+               /* FALLTHROUGH*/
+       case (ROFF_Sx):
+               /* FALLTHROUGH*/
+       case (ROFF_Ex):
+               if (1 == sz) 
+                       break;
+               roff_err(tree, start, "`%s' expects one arg", 
+                               toknames[tok]);
+               return(0);
+
+       case (ROFF_Sm):
+               if (1 != sz) {
+                       roff_err(tree, start, "`Sm' expects one arg");
+                       return(0);
+               } 
+               
+               if (0 != strcmp(ordp[0], "on") &&
+                               0 != strcmp(ordp[0], "off")) {
+                       roff_err(tree, start, "`Sm' has invalid argument");
+                       return(0);
+               }
+               break;
+       
+       case (ROFF_Ud):
+               /* FALLTHROUGH */
+       case (ROFF_Ux):
+               /* FALLTHROUGH */
+       case (ROFF_Bt):
+               if (0 != sz) {
+                       roff_err(tree, start, "`%s' expects no args",
+                                       toknames[tok]);
+                       return(0);
+               }
+               break;
+       default:
+               break;
+       }
+
        return((*tree->cb.roffspecial)(tree->arg, tok, 
-                               tree->cur, ordp));
+                               tree->cur, argc, argv, ordp));
 }
 
 
@@ -1029,7 +849,7 @@ roff_Dd(ROFFCALL_ARGS)
 
        argv++;
 
-       if (0 == strcmp(*argv, "$Mdocdate: December 4 2008 $")) {
+       if (0 == strcmp(*argv, "$Mdocdate: December 7 2008 $")) {
                t = time(NULL);
                if (NULL == localtime_r(&t, &tree->tm))
                        err(1, "localtime_r");
@@ -1144,33 +964,25 @@ roff_Dt(ROFFCALL_ARGS)
 }
 
 
-/* ARGSUSED */
-static int
-roff_Sm(ROFFCALL_ARGS)
+static int
+roffsetname(struct rofftree *tree, char **ordp)
 {
-       char            *morep[1], *p;
+       
+       assert(*ordp);
 
-       p = *argv++;
+       /* FIXME: not all sections can set this. */
 
-       if (NULL == (morep[0] = *argv++)) {
-               roff_err(tree, p, "`Sm' expects an argument");
+       if (NULL != *(ordp + 1)) {
+               roff_err(tree, *ordp, "too many `Nm' args");
                return(0);
-       } else if (0 != strcmp(morep[0], "on") && 
-                       0 != strcmp(morep[0], "off")) {
-               roff_err(tree, p, "`Sm' has invalid argument");
+       } 
+       
+       if (strlcpy(tree->name, *ordp, sizeof(tree->name)) 
+                       >= sizeof(tree->name)) {
+               roff_err(tree, *ordp, "`Nm' arg too long");
                return(0);
        }
 
-       if (*argv) 
-               roff_warn(tree, *argv, "`Sm' shouldn't have arguments");
-
-       if ( ! roffspecial(tree, tok, morep))
-               return(0);
-
-       while (*argv)
-               if ( ! roffdata(tree, 1, *argv++))
-                       return(0);
-
        return(1);
 }
 
@@ -1185,7 +997,7 @@ roff_Ns(ROFFCALL_ARGS)
        first = (*argv++ == tree->cur);
        morep[0] = NULL;
 
-       if ( ! roffspecial(tree, tok, morep))
+       if ( ! roffspecial(tree, tok, *argv, NULL, NULL, 0, morep))
                return(0);
 
        while (*argv) {
@@ -1380,8 +1192,9 @@ roff_layout(ROFFCALL_ARGS)
 static int
 roff_ordered(ROFFCALL_ARGS) 
 {
-       int              i, first, c;
-       char            *ordp[ROFF_MAXLINEARG];
+       int              i, first, c, argcp[ROFF_MAXLINEARG];
+       char            *ordp[ROFF_MAXLINEARG], *p,
+                       *argvp[ROFF_MAXLINEARG];
 
        if (ROFF_PRELUDE & tree->state) {
                roff_err(tree, *argv, "`%s' disallowed in prelude", 
@@ -1390,40 +1203,49 @@ roff_ordered(ROFFCALL_ARGS)
        }
 
        first = (*argv == tree->cur);
-       argv++;
-
-       if (NULL == *argv) {
+       p = *argv++;
 
-               /* FIXME: satisfies number of args? */
+       if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
+               return(0);
 
+       if (NULL == *argv) {
                ordp[0] = NULL;
-               return(roffspecial(tree, tok, ordp));
+               return(roffspecial(tree, tok, p, argcp, 
+                                       (const char **)argvp, 0, ordp));
        }
 
        i = 0;
        while (*argv && i < ROFF_MAXLINEARG) {
-               if (ROFF_MAX != (c = rofffindcallable(*argv)))
-                       return(roffcall(tree, c, argv));
-               if (roffispunct(*argv)) 
+               c = ROFF_PARSED & tokens[tok].flags ?
+                       rofffindcallable(*argv) : ROFF_MAX;
+
+               if (ROFF_MAX == c && ! roffispunct(*argv)) {
+                       ordp[i++] = *argv++;
+                       continue;
+               }
+               ordp[i] = NULL;
+
+               if (ROFF_MAX == c)
                        break;
 
-               ordp[i++] = *argv++;
+               if ( ! roffspecial(tree, tok, p, argcp, 
+                                       (const char **)argvp,
+                                       (size_t)i, ordp))
+                       return(0);
+
+               return(roffcall(tree, c, argv));
        }
 
+       assert(i != ROFF_MAXLINEARG);
        ordp[i] = NULL;
 
-       /* FIXME: too many or too few args? */
-
-       if (i == ROFF_MAXLINEARG && *argv) {
-               roff_err(tree, *argv, "too many args", toknames[tok]);
+       if ( ! roffspecial(tree, tok, p, argcp, 
+                               (const char**)argvp,
+                               (size_t)i, ordp))
                return(0);
-       }
 
        /* FIXME: error if there's stuff after the punctuation. */
 
-       if ( ! roffspecial(tree, tok, ordp))
-               return(0);
-
        if ( ! first || NULL == *argv)
                return(1);
 
@@ -1468,6 +1290,8 @@ roff_text(ROFFCALL_ARGS)
         * terminating punctuation.  If we encounter it and all
         * subsequent tokens are punctuation, then stop processing (the
         * line-dominant macro will print these tokens after closure).
+        * If the punctuation is followed by non-punctuation, then close
+        * and re-open our scope, then continue.
         */
 
        i = 0;
@@ -1499,8 +1323,20 @@ roff_text(ROFFCALL_ARGS)
                                break;
 
                if (argv[j]) {
+                       if (ROFF_LSCOPE & tokens[tok].flags) {
+                               if ( ! roffdata(tree, 0, *argv++))
+                                       return(0);
+                               continue;
+                       }
+                       if ( ! (*tree->cb.roffout)(tree->arg, tok))
+                               return(0);
                        if ( ! roffdata(tree, 0, *argv++))
                                return(0);
+                       if ( ! (*tree->cb.roffin)(tree->arg, tok,
+                                               argcp, argvp))
+                               return(0);
+
+                       i = 0;
                        continue;
                }