diff options
-rw-r--r-- | mdoc.3 | 123 | ||||
-rw-r--r-- | mdoc.c | 3 | ||||
-rw-r--r-- | mdocml.1 | 4 | ||||
-rw-r--r-- | mdocml.c | 143 | ||||
-rw-r--r-- | tree.c | 101 | ||||
-rw-r--r-- | validate.c | 136 |
6 files changed, 366 insertions, 144 deletions
@@ -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. @@ -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; @@ -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: @@ -36,38 +36,42 @@ #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); } @@ -0,0 +1,101 @@ +/* $Id$ */ +/* + * 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) +{ +} @@ -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); } @@ -300,6 +308,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 |