]> git.cameronkatri.com Git - mandoc.git/commitdiff
Initial separation of tree/mdocml.1.
authorKristaps Dzonsons <kristaps@bsd.lv>
Fri, 16 Jan 2009 15:58:50 +0000 (15:58 +0000)
committerKristaps Dzonsons <kristaps@bsd.lv>
Fri, 16 Jan 2009 15:58:50 +0000 (15:58 +0000)
Finished mdoc.3.
Broken build: mdocml.c.

mdoc.3
mdoc.c
mdocml.1
mdocml.c
tree.c [new file with mode: 0644]
validate.c

diff --git a/mdoc.3 b/mdoc.3
index 0df0188aab37f308b1e4c30ade1ed09f73567188..0adcb26cf0fa44c53dd5f9723338a8976a655ee1 100644 (file)
--- a/mdoc.3
+++ b/mdoc.3
@@ -9,20 +9,20 @@
 .Nm mdoc_endparse ,
 .Nm mdoc_result ,
 .Nm mdoc_free
-.Nd mdoc macro compiler
+.Nd mdoc macro compiler library
 .\"
 .Sh SYNOPSIS
 .In mdoc.h
 .Ft "struct mdoc *"
 .Fn mdoc_alloc "void *data" "const struct mdoc_cb *cb"
 .Ft void
-.Fn mdoc_free "struct mdoc *"
+.Fn mdoc_free "struct mdoc *mdoc"
 .Ft int
-.Fn mdoc_parseln "struct mdoc *" "int" "char *buf"
+.Fn mdoc_parseln "struct mdoc *mdoc" "int line" "char *buf"
 .Ft "const struct mdoc_node *"
-.Fn mdoc_result "struct mdoc *"
+.Fn mdoc_result "struct mdoc *mdoc"
 .Ft int
-.Fn mdoc_endparse "struct mdoc *"
+.Fn mdoc_endparse "struct mdoc *mdoc"
 .\"
 .Sh DESCRIPTION
 The
@@ -41,16 +41,103 @@ then free all allocated memory with
 See the
 .Sx EXAMPLES
 section for a full example.
-.\" The following requests should be uncommented and used where appropriate.
-.\" This next request is for sections 2, 3, and 9 function return values only.
-.\" .Sh RETURN VALUES
-.\" .Sh EXAMPLES
-.\" The next request is for sections 2, 3, and 9 error and signal handling only.
-.\" .Sh ERRORS
-.\" .Sh SEE ALSO
-.\" .Xr foobar 1
-.\" .Sh STANDARDS
-.\" .Sh HISTORY
-.\" .Sh AUTHORS
-.\" .Sh CAVEATS
-.\" .Sh BUGS
+.Pp
+Function descriptions follow:
+.Bl -ohang -offset indent
+.It Fn mdoc_alloc
+Allocates a parsing structure.  The
+.Fa data
+pointer is passed to callbacks in
+.Fa cb , 
+which are documented further in the header file.  Returns NULL on
+failure.  If non-NULL, the pointer must be freed with
+.Fn mdoc_free .
+.It Fn mdoc_free
+Free all resources of a parser.  The pointer is no longer valid after
+invocation.
+.It Fn mdoc_parseln
+Parse a nil-terminated line of input.  This line should not contain the
+trailing newline.  Returns 0 on failure, 1 on success.  The input buffer 
+.Fa buf
+is modified by this function.
+.It Fn mdoc_endparse
+Signals that the parse is complete.  Note that if 
+.Fn mdoc_endparse
+is called subsequent to
+.Fn mdoc_result ,
+the resulting tree is incomplete.  Returns 0 on failure, 1 on success.
+.It Fn mdoc_result
+Returns the result of the parse or NULL on failure.  Note that if 
+.Fn mdoc_parseln
+or
+.Fn mdoc_endparse
+return 0, the tree will be incomplete.
+.El
+.Pp
+.Nm
+is
+.Ud
+.\" 
+.Sh EXAMPLES
+The following example reads lines from stdin and parses them, operating
+on the finished parse tree with 
+.Fn parsed .
+Note that, if the last line of the file isn't newline-terminated, this
+will truncate the file's last character (see 
+.Xr fgetln 3 ) .
+Further, this example does not error-check nor free memory upon failure.
+.Bd -literal
+struct mdoc *mdoc;
+struct mdoc_node *node;
+char *buf;
+size_t len;
+int line;
+
+line = 1;
+mdoc = mdoc_alloc(NULL, NULL);
+
+while ((buf = fgetln(fp, &len))) {
+       buf[len - 1] = '\\0';
+       if ( ! mdoc_parseln(mdoc, line, buf))
+               errx(1, "mdoc_parseln");
+       line++;
+}
+
+if ( ! mdoc_endparse(mdoc))
+       errx(1, "mdoc_endparse");
+if (NULL == (node = mdoc_result(mdoc)))
+       errx(1, "mdoc_result");
+
+parsed(mdoc, node);
+mdoc_free(mdoc);
+.Ed
+.\"
+.Sh SEE ALSO
+.Xr mdoc 7 ,
+.Xr mdoc.samples 7 ,
+.Xr groff 1 ,
+.Xr mdocml 1
+.\"
+.\"
+.Sh AUTHORS
+The
+.Nm
+utility was written by 
+.An Kristaps Dzonsons Aq kristaps@kth.se .
+.\"
+.\"
+.Sh BUGS
+The 
+.Sq \&Xc
+and
+.Sq \&Xo
+macros aren't handled when used to span lines for the
+.Sq \&It
+macro.  Such usage is specifically discouraged in
+.Xr mdoc.samples 7 .
+.Pp
+When 
+.Sq \&It \-column
+is invoked, whitespace is not stripped around
+.Sq \&Ta
+or tab-character separators.
diff --git a/mdoc.c b/mdoc.c
index 88260b79a9416e7079f2102a83e4d8654342b0e6..51669d5394f4c1bf665ba7a94af05e9ad5d77bb2 100644 (file)
--- a/mdoc.c
+++ b/mdoc.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc.c,v 1.32 2009/01/16 14:04:26 kristaps Exp $ */
+/* $Id: mdoc.c,v 1.33 2009/01/16 15:58:50 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -241,7 +241,8 @@ mdoc_alloc(void *data, const struct mdoc_cb *cb)
        p = xcalloc(1, sizeof(struct mdoc));
 
        p->data = data;
-       (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
+       if (cb)
+               (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
 
        p->last = xcalloc(1, sizeof(struct mdoc_node));
        p->last->type = MDOC_ROOT;
index a43b19b04cc46f287aa82f0f427fec312871def2..900b673627224988b545ba7349defe0f076ebc4a 100644 (file)
--- a/mdocml.1
+++ b/mdocml.1
@@ -56,6 +56,10 @@ reads from stdin, writes messages to stdout, and writes errors and
 warnings to stderr.
 .Pp
 .Ex -std mdocml
+.Pp
+.Nm
+is
+.Ud
 .\" 
 .Sh EXAMPLES
 To validate this manual page:
index 2b7c4388fb4f9bde9142d0a3f9ddaddeae43d20f..26aa81ea7c460f6f1e8e1882b4a18cac5b657dca 100644 (file)
--- a/mdocml.c
+++ b/mdocml.c
@@ -1,4 +1,4 @@
-/* $Id: mdocml.c,v 1.46 2009/01/16 14:15:12 kristaps Exp $ */
+/* $Id: mdocml.c,v 1.47 2009/01/16 15:58:50 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
 
 #define        MD_LINE_SZ      (256)           /* Max input line size. */
 
+
 struct md_parse {
-       int              warn;          /* Warning flags. */
-#define        MD_WARN_SYNTAX  (1 << 0)        /* Show syntax warnings. */
-#define        MD_WARN_COMPAT  (1 << 1)        /* Show compat warnings. */
-#define        MD_WARN_ALL     (0x03)          /* Show all warnings. */
-#define        MD_WARN_ERR     (1 << 2)        /* Make warnings->errors. */
-       int              dbg;           /* Debug level. */
-       struct mdoc     *mdoc;          /* Active parser. */
-       char            *buf;           /* Input buffer. */
-       u_long           bufsz;         /* Input buffer size. */
-       char            *name;          /* Input file name. */
-       int              fd;            /* Input file desc. */
+       int               warn;         /* Warning flags. */
+#define        MD_WARN_SYNTAX   (1 << 0)       /* Show syntax warnings. */
+#define        MD_WARN_COMPAT   (1 << 1)       /* Show compat warnings. */
+#define        MD_WARN_ALL      (0x03)         /* Show all warnings. */
+#define        MD_WARN_ERR      (1 << 2)       /* Make warnings->errors. */
+       int               dbg;          /* Debug level. */
+       struct mdoc      *mdoc;         /* Active parser. */
+       char             *buf;          /* Input buffer. */
+       u_long            bufsz;        /* Input buffer size. */
+       char             *name;         /* Input file name. */
+       int               fd;           /* Input file desc. */
+       int             (*fp)(const struct mdoc_node *, const char *);
 };
 
-extern char            *__progname;
+extern char             *__progname;
+
+extern int              
 
-static void             usage(void);
+static void              usage(void);
 
-static int              parse_begin(struct md_parse *);
-static int              parse_leave(struct md_parse *, int);
-static int              io_begin(struct md_parse *);
-static int              io_leave(struct md_parse *, int);
-static int              buf_begin(struct md_parse *);
-static int              buf_leave(struct md_parse *, int);
+static int               parse_begin(struct md_parse *);
+static int               parse_leave(struct md_parse *, int);
+static int               io_begin(struct md_parse *);
+static int               io_leave(struct md_parse *, int);
+static int               buf_begin(struct md_parse *);
+static int               buf_leave(struct md_parse *, int);
 
-static void             msg_msg(void *, int, int, const char *);
-static int              msg_err(void *, int, int, const char *);
-static int              msg_warn(void *, int, int, 
+static void              msg_msg(void *, int, int, const char *);
+static int               msg_err(void *, int, int, const char *);
+static int               msg_warn(void *, int, int, 
                                enum mdoc_warn, const char *);
 
 #ifdef __linux__
-extern int              getsubopt(char **, char *const *, char **);
+extern int               getsubopt(char **, char *const *, char **);
 #endif
 
 int
@@ -75,7 +79,7 @@ main(int argc, char *argv[])
 {
        int              c;
        struct md_parse  parser;
-       char            *opts, *v;
+       char            *opts, *v, *filter, *output;
 #define ALL             0
 #define COMPAT          1
 #define SYNTAX          2
@@ -86,10 +90,18 @@ main(int argc, char *argv[])
        extern char     *optarg;
        extern int       optind;
 
+       output = filter = NULL;
+
        (void)memset(&parser, 0, sizeof(struct md_parse));
 
-       while (-1 != (c = getopt(argc, argv, "vW:")))
+       while (-1 != (c = getopt(argc, argv, "f:vW:o:")))
                switch (c) {
+               case ('f'):
+                       filter = optarg;
+                       break;
+               case ('o'):
+                       output = optarg;
+                       break;
                case ('v'):
                        parser.dbg++;
                        break;
@@ -194,79 +206,6 @@ buf_begin(struct md_parse *p)
 }
 
 
-/* TODO: remove this to a print-tree output filter. */
-static void
-print_node(const struct mdoc_node *n, int indent)
-{
-       const char       *p, *t;
-       int               i, j;
-       size_t            argc, sz;
-       char            **params;
-       struct mdoc_arg  *argv;
-
-       argv = NULL;
-       argc = sz = 0;
-       params = NULL;
-
-       t = mdoc_type2a(n->type);
-
-       switch (n->type) {
-       case (MDOC_TEXT):
-               p = n->data.text.string;
-               break;
-       case (MDOC_BODY):
-               p = mdoc_macronames[n->tok];
-               break;
-       case (MDOC_HEAD):
-               p = mdoc_macronames[n->tok];
-               break;
-       case (MDOC_TAIL):
-               p = mdoc_macronames[n->tok];
-               break;
-       case (MDOC_ELEM):
-               p = mdoc_macronames[n->tok];
-               argv = n->data.elem.argv;
-               argc = n->data.elem.argc;
-               break;
-       case (MDOC_BLOCK):
-               p = mdoc_macronames[n->tok];
-               argv = n->data.block.argv;
-               argc = n->data.block.argc;
-               break;
-       case (MDOC_ROOT):
-               p = "root";
-               break;
-       default:
-               abort();
-               /* NOTREACHED */
-       }
-
-       for (i = 0; i < indent; i++)
-               xprintf("    ");
-       xprintf("%s (%s)", p, t);
-
-       for (i = 0; i < (int)argc; i++) {
-               xprintf(" -%s", mdoc_argnames[argv[i].arg]);
-               if (argv[i].sz > 0)
-                       xprintf(" [");
-               for (j = 0; j < (int)argv[i].sz; j++)
-                       xprintf(" [%s]", argv[i].value[j]);
-               if (argv[i].sz > 0)
-                       xprintf(" ]");
-       }
-
-       for (i = 0; i < (int)sz; i++)
-               xprintf(" [%s]", params[i]);
-
-       xprintf(" %d:%d\n", n->line, n->pos);
-
-       if (n->child)
-               print_node(n->child, indent + 1);
-       if (n->next)
-               print_node(n->next, indent);
-}
-
-
 static int
 parse_leave(struct md_parse *p, int code)
 {
@@ -277,8 +216,8 @@ parse_leave(struct md_parse *p, int code)
 
        if ( ! mdoc_endparse(p->mdoc))
                code = 0;
-       if ((n = mdoc_result(p->mdoc)))
-               print_node(n, 0);
+       if (p->fp && (n = mdoc_result(p->mdoc)))
+               (*p->fp)(n, NULL);
 
        mdoc_free(p->mdoc);
 
@@ -396,7 +335,7 @@ static void
 usage(void)
 {
 
-       xfprintf(stderr, "usage: %s [-v] [-Wwarn...] [infile]\n",
-                       __progname);
+       xfprintf(stderr, "usage: %s [-v] [-Wwarn...] [-ffilter] "
+                       "[-o outfile] [infile]\n", __progname);
 }
 
diff --git a/tree.c b/tree.c
new file mode 100644 (file)
index 0000000..484b9bf
--- /dev/null
+++ b/tree.c
@@ -0,0 +1,101 @@
+/* $Id: tree.c,v 1.1 2009/01/16 15:58:50 kristaps Exp $ */
+/*
+ * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
+ *
+ * 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 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.
+ */
+#include <stdlib.h>
+
+#include "mdoc.h"
+
+
+#if 0
+/* TODO: remove this to a print-tree output filter. */
+static void
+print_node(const struct mdoc_node *n, int indent)
+{
+       const char       *p, *t;
+       int               i, j;
+       size_t            argc, sz;
+       char            **params;
+       struct mdoc_arg  *argv;
+
+       argv = NULL;
+       argc = sz = 0;
+       params = NULL;
+
+       t = mdoc_type2a(n->type);
+
+       switch (n->type) {
+       case (MDOC_TEXT):
+               p = n->data.text.string;
+               break;
+       case (MDOC_BODY):
+               p = mdoc_macronames[n->tok];
+               break;
+       case (MDOC_HEAD):
+               p = mdoc_macronames[n->tok];
+               break;
+       case (MDOC_TAIL):
+               p = mdoc_macronames[n->tok];
+               break;
+       case (MDOC_ELEM):
+               p = mdoc_macronames[n->tok];
+               argv = n->data.elem.argv;
+               argc = n->data.elem.argc;
+               break;
+       case (MDOC_BLOCK):
+               p = mdoc_macronames[n->tok];
+               argv = n->data.block.argv;
+               argc = n->data.block.argc;
+               break;
+       case (MDOC_ROOT):
+               p = "root";
+               break;
+       default:
+               abort();
+               /* NOTREACHED */
+       }
+
+       for (i = 0; i < indent; i++)
+               xprintf("    ");
+       xprintf("%s (%s)", p, t);
+
+       for (i = 0; i < (int)argc; i++) {
+               xprintf(" -%s", mdoc_argnames[argv[i].arg]);
+               if (argv[i].sz > 0)
+                       xprintf(" [");
+               for (j = 0; j < (int)argv[i].sz; j++)
+                       xprintf(" [%s]", argv[i].value[j]);
+               if (argv[i].sz > 0)
+                       xprintf(" ]");
+       }
+
+       for (i = 0; i < (int)sz; i++)
+               xprintf(" [%s]", params[i]);
+
+       xprintf(" %d:%d\n", n->line, n->pos);
+
+       if (n->child)
+               print_node(n->child, indent + 1);
+       if (n->next)
+               print_node(n->next, indent);
+}
+#endif
+
+int
+treeprint(const struct mdoc_node *node, const char *out)
+{
+}
index d541f0c04e29761d2b233ebd8e95feb2b9c35c00..d7413fa5dca16dbf9f4e8aaa08b297e9dc60aa46 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: validate.c,v 1.34 2009/01/16 14:15:12 kristaps Exp $ */
+/* $Id: validate.c,v 1.35 2009/01/16 15:58:50 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -45,6 +45,7 @@ static        int     pre_it(struct mdoc *, struct mdoc_node *);
 static int     pre_cd(struct mdoc *, struct mdoc_node *);
 static int     pre_er(struct mdoc *, struct mdoc_node *);
 static int     pre_ex(struct mdoc *, struct mdoc_node *);
+static int     pre_an(struct mdoc *, struct mdoc_node *);
 static int     pre_prologue(struct mdoc *, struct mdoc_node *);
 static int     pre_prologue(struct mdoc *, struct mdoc_node *);
 static int     pre_prologue(struct mdoc *, struct mdoc_node *);
@@ -53,6 +54,7 @@ static        int     head_err_ge1(struct mdoc *);
 static int     head_warn_ge1(struct mdoc *);
 static int     head_err_eq0(struct mdoc *);
 static int     elem_err_eq0(struct mdoc *);
+static int     elem_err_le1(struct mdoc *);
 static int     elem_err_eq1(struct mdoc *);
 static int     elem_err_ge1(struct mdoc *);
 static int     elem_warn_eq0(struct mdoc *);
@@ -63,6 +65,8 @@ static        int     elem_bool(struct mdoc *);
 static int     post_sh(struct mdoc *);
 static int     post_bl(struct mdoc *);
 static int     post_it(struct mdoc *);
+static int     post_ex(struct mdoc *);
+static int     post_an(struct mdoc *);
 
 static v_pre   pres_prologue[] = { pre_prologue, NULL };
 static v_pre   pres_d1[] = { pre_display, NULL };
@@ -74,6 +78,7 @@ static        v_pre   pres_sh[] = { pre_sh, NULL };
 static v_pre   pres_cd[] = { pre_cd, NULL };
 static v_pre   pres_er[] = { pre_er, NULL };
 static v_pre   pres_ex[] = { pre_ex, NULL };
+static v_pre   pres_an[] = { pre_an, NULL };
 
 static v_post  posts_bool[] = { elem_err_eq1, elem_bool, NULL };
 static v_post  posts_bd[] = { head_err_eq0, body_warn_ge1, NULL };
@@ -89,6 +94,8 @@ static        v_post  posts_it[] = { post_it, NULL };
 static v_post  posts_ss[] = { head_err_ge1, NULL };
 static v_post  posts_pp[] = { elem_warn_eq0, NULL };
 static v_post  posts_d1[] = { head_err_ge1, NULL };
+static v_post  posts_ex[] = { elem_err_le1, post_ex, NULL };
+static v_post  posts_an[] = { post_an, NULL };
 
 
 const  struct valids mdoc_valids[MDOC_MAX] = {
@@ -101,7 +108,7 @@ const       struct valids mdoc_valids[MDOC_MAX] = {
        { pres_sh, posts_sh }, /* Sh */ 
        /* FIXME: preceding Pp. */
        { pres_ss, posts_ss }, /* Ss */ 
-       /* FIXME: proceeding... */
+       /* FIXME: proceeding Pp */
        { NULL, posts_pp }, /* Pp */ 
        { pres_d1, posts_d1 }, /* D1 */
        { pres_d1, posts_d1 }, /* Dl */
@@ -113,17 +120,17 @@ const     struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, NULL }, /* El */
        { pres_it, posts_it }, /* It */
        { NULL, posts_text }, /* Ad */ 
-       /* FIXME: argument OR parameters. */
-       { NULL, NULL }, /* An */ 
+       { pres_an, posts_an }, /* An */ 
        { NULL, NULL }, /* Ar */
        { pres_cd, posts_text }, /* Cd */ 
        { NULL, NULL }, /* Cm */
        { NULL, posts_text }, /* Dv */ 
        { pres_er, posts_text }, /* Er */ 
        { NULL, posts_text }, /* Ev */ 
-       { pres_ex, posts_notext }, /* Ex */ /* FIXME: -std required */
+       { pres_ex, posts_ex }, /* Ex */ 
        { NULL, posts_text }, /* Fa */ 
-       { NULL, NULL }, /* Fd */ /* FIXME: SYNOPSIS section. */
+       /* FIXME: only in SYNOPSIS section. */
+       { NULL, NULL }, /* Fd */
        { NULL, NULL }, /* Fl */
        { NULL, posts_text }, /* Fn */ 
        { NULL, NULL }, /* Ft */ 
@@ -131,15 +138,16 @@ const     struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, posts_wtext }, /* In */ 
        { NULL, posts_text }, /* Li */
        { NULL, posts_wtext }, /* Nd */
-       { NULL, NULL }, /* Nm */  /* FIXME: If name not set? */
+       /* FIXME: check that name must be set/provided. */
+       { NULL, NULL }, /* Nm */
        { NULL, posts_wline }, /* Op */
        { NULL, NULL }, /* Ot */
        { NULL, NULL }, /* Pa */
-       { NULL, posts_notext }, /* Rv */ /* -std required */
-       { NULL, posts_notext }, /* St */ /* arg required */
+       { NULL, posts_notext }, /* Rv */ /* FIXME: -std required */
+       { NULL, posts_notext }, /* St */ /* FIXME: arg required */
        { NULL, posts_text }, /* Va */
        { NULL, posts_text }, /* Vt */ 
-       { NULL, NULL }, /* Xr */ /* FIXME */
+       { NULL, NULL }, /* Xr */ /* FIXME: valid arguments */
        { NULL, posts_text }, /* %A */
        { NULL, posts_text }, /* %B */
        { NULL, posts_text }, /* %D */
@@ -154,7 +162,7 @@ const       struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, NULL }, /* Ac */
        { NULL, NULL }, /* Ao */
        { NULL, posts_wline }, /* Aq */
-       { NULL, NULL }, /* At */ /* FIXME */
+       { NULL, NULL }, /* At */ /* FIXME: valid arguments */
        { NULL, NULL }, /* Bc */
        { NULL, NULL }, /* Bf */ 
        { NULL, NULL }, /* Bo */
@@ -176,7 +184,7 @@ const       struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, NULL }, /* Nx */
        { NULL, NULL }, /* Ox */
        { NULL, NULL }, /* Pc */
-       { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */ /* First should be text. */
+       { NULL, NULL }, /* Pf */
        { NULL, NULL }, /* Po */
        { NULL, posts_wline }, /* Pq */ /* FIXME: ignore following Sh/Ss */
        { NULL, NULL }, /* Qc */
@@ -227,13 +235,13 @@ pre_check_parent(struct mdoc *mdoc, struct mdoc_node *node,
                int tok, enum mdoc_type type)
 {
 
-       if (type != mdoc->last->parent->type) 
+       if (type != node->parent->type) 
                return(mdoc_nerr(mdoc, node, "invalid macro parent class %s, expected %s", 
-                                       mdoc_type2a(mdoc->last->parent->type),
+                                       mdoc_type2a(node->parent->type),
                                        mdoc_type2a(type)));
-       if (MDOC_ROOT != type && tok == mdoc->last->parent->tok)
+       if (MDOC_ROOT != type && tok != node->parent->tok)
                return(mdoc_nerr(mdoc, node, "invalid macro parent `%s', expected `%s'", 
-                                       mdoc_macronames[mdoc->last->parent->tok],
+                                       mdoc_macronames[node->parent->tok],
                                        mdoc_macronames[tok]));
        return(1);
 }
@@ -299,6 +307,19 @@ elem_err_eq1(struct mdoc *mdoc)
 }
 
 
+static int
+elem_err_le1(struct mdoc *mdoc)
+{
+
+       assert(MDOC_ELEM == mdoc->last->type);
+       if (NULL == mdoc->last->child)
+               return(1);
+       if (NULL == mdoc->last->child->next)
+               return(1);
+       return(mdoc_err(mdoc, "macro expects one or fewer parameters"));
+}
+
+
 static int
 elem_err_eq0(struct mdoc *mdoc)
 {
@@ -483,9 +504,8 @@ static int
 pre_ss(struct mdoc *mdoc, struct mdoc_node *node)
 {
 
-       if (MDOC_BLOCK != mdoc->last->type)
+       if (MDOC_BLOCK != node->type)
                return(1);
-       assert(MDOC_Sh == mdoc->last->tok);
        return(pre_check_parent(mdoc, node, MDOC_Sh, MDOC_BODY));
 }
 
@@ -494,22 +514,49 @@ static int
 pre_sh(struct mdoc *mdoc, struct mdoc_node *node)
 {
 
-       if (MDOC_BLOCK != mdoc->last->type)
+       if (MDOC_BLOCK != node->type)
                return(1);
-       assert(MDOC_Sh == mdoc->last->tok);
        return(pre_check_parent(mdoc, node, -1, MDOC_ROOT));
 }
 
 
+static int
+pre_an(struct mdoc *mdoc, struct mdoc_node *node)
+{
+       assert(MDOC_ELEM == node->type);
+       assert(MDOC_An == node->tok);
+       if (1 >= node->data.elem.argc)
+               return(1);
+       return(mdoc_nerr(mdoc, node, "macro may only have one argument"));
+}
+
+
 static int
 pre_ex(struct mdoc *mdoc, struct mdoc_node *node)
 {
        enum mdoc_msec   msecs[3];
 
+       assert(MDOC_ELEM == node->type);
+
        msecs[0] = MSEC_1;
        msecs[1] = MSEC_6;
        msecs[2] = MSEC_8;
-       return(pre_check_msecs(mdoc, node, 3, msecs));
+       if ( ! pre_check_msecs(mdoc, node, 3, msecs))
+               return(0);
+
+       if (1 != node->data.elem.argc) {
+               if ( ! mdoc_nwarn(mdoc, node, WARN_COMPAT, 
+                                       "macro suggests `%s' argument",
+                                       mdoc_argnames[MDOC_Std]))
+                       return(0);
+               return(1);
+       }
+       if (MDOC_Std != node->data.elem.argv[0].arg)
+               if ( ! mdoc_nwarn(mdoc, node, WARN_COMPAT, 
+                                       "macro suggests `%s' argument",
+                                       mdoc_argnames[MDOC_Std]))
+                       return(0);
+       return(1);
 }
 
 
@@ -537,9 +584,8 @@ static int
 pre_it(struct mdoc *mdoc, struct mdoc_node *node)
 {
 
-       if (MDOC_BLOCK != mdoc->last->type)
+       if (MDOC_BLOCK != node->type)
                return(1);
-       assert(MDOC_It == mdoc->last->tok);
        return(pre_check_parent(mdoc, node, MDOC_Bl, MDOC_BODY));
 }
 
@@ -596,6 +642,51 @@ pre_prologue(struct mdoc *mdoc, struct mdoc_node *node)
 }
 
 
+static int
+post_an(struct mdoc *mdoc)
+{
+
+       assert(MDOC_ELEM == mdoc->last->type);
+       assert(MDOC_An == mdoc->last->tok);
+
+       if (0 != mdoc->last->data.elem.argc) {
+               if (NULL == mdoc->last->child)
+                       return(1);
+               return(mdoc_err(mdoc, "macro expects either argument or parameters"));
+       }
+
+       if (mdoc->last->child)
+               return(1);
+       return(mdoc_err(mdoc, "macro expects either argument or parameters"));
+}
+
+
+static int
+post_ex(struct mdoc *mdoc)
+{
+
+       assert(MDOC_ELEM == mdoc->last->type);
+       assert(MDOC_Ex == mdoc->last->tok);
+
+       if (0 == mdoc->last->data.elem.argc) {
+               if (mdoc->last->child)
+                       return(1);
+               return(mdoc_err(mdoc, "macro expects `%s' or a single child",
+                                       mdoc_argnames[MDOC_Std]));
+       }
+       if (mdoc->last->child)
+               return(mdoc_err(mdoc, "macro expects `%s' or a single child",
+                                       mdoc_argnames[MDOC_Std]));
+       if (1 != mdoc->last->data.elem.argc)
+               return(mdoc_err(mdoc, "macro expects `%s' or a single child",
+                                       mdoc_argnames[MDOC_Std]));
+       if (MDOC_Std != mdoc->last->data.elem.argv[0].arg)
+               return(mdoc_err(mdoc, "macro expects `%s' or a single child",
+                                       mdoc_argnames[MDOC_Std]));
+       return(1);
+}
+
+
 /* Warn if `Bl' type-specific syntax isn't reflected in items. */
 static int
 post_it(struct mdoc *mdoc)
@@ -704,7 +795,6 @@ post_it(struct mdoc *mdoc)
        if (i == (size_t)sv)
                return(1);
        return(mdoc_err(mdoc, "expected %d list columns, have %d", sv, (int)i));
-
 #undef TYPE_NONE
 #undef TYPE_BODY
 #undef TYPE_HEAD