-/* $Id: mdoc.c,v 1.31 2009/01/15 17:38:57 kristaps Exp $ */
+/* $Id: mdoc.c,v 1.32 2009/01/16 14:04:26 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
{ macro_scoped_close, MDOC_EXPLICIT }, /* El */
{ macro_scoped, MDOC_PARSED | MDOC_TABSEP}, /* It */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
- { macro_constant, MDOC_PARSED }, /* An */
+ { macro_text, MDOC_PARSED }, /* An */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
{ macro_constant, MDOC_QUOTABLE }, /* Cd */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
{ macro_constant, 0 }, /* Fd */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
{ macro_text, MDOC_CALLABLE | MDOC_QUOTABLE | MDOC_PARSED }, /* Fn */
- { macro_text, MDOC_PARSED }, /* Ft */
+ { macro_text, MDOC_PARSED | MDOC_QUOTABLE }, /* Ft */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
{ macro_constant, 0 }, /* In */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
{ macro_constant_delimited, MDOC_PARSED }, /* Nx */
{ macro_constant_delimited, MDOC_PARSED }, /* Ox */
{ macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
- { macro_constant, MDOC_PARSED }, /* Pf */
+ { macro_constant_delimited, MDOC_PARSED }, /* Pf */
{ macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
{ macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
{ macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
.\"
-.Dd $Mdocdate: December 10 2008 $
+.Dd $Mdocdate: January 16 2009 $
.Dt mdocml 1
.Os
.\"
.Sh NAME
.Nm mdocml
-.Nd compile manpage source into mark-up language
+.Nd mdoc macro compiler
.\"
.Sh SYNOPSIS
.Nm mdocml
.Op Fl v
.Op Fl W Ns Ar err...
-.Op Fl f Ar filter
-.Op Fl o Ar outfile
.Op Ar infile
.\"
.Sh DESCRIPTION
The
.Nm
-utility parses mdoc formatted manual source and passes results into an
-output filter. The current output filters are
-.Fl f Ar html
-and
-.Fl f Ar xml .
-By default,
-.Nm
-only validates its input. This may be explicitly directed with
-.Fl f Ar noop .
-Arguments common to all filters follow:
-.Bl -tag -width "\-o outfile"
-.It Fl f Ar filter
-The output filter name.
-.It Fl o Ar outfile
-Write output to
-.Ar outfile ,
-which may be
-.Dq \-
-for stdout. This is meaningless for
-.Fl f Ar noop .
+utility interfaces the
+.Xr mdoc 3
+library to validate and parse mdoc-macro documents. Arguments follow:
+.Bl -tag -width "\-Werr... "
.It Fl W Ns Ar err...
-Print warning messages. If set to
-.Fl W Ns Ar all ,
-all warnings are printed; if
-.Fl W Ns Ar error ,
-warnings are considered errors and cause utility termination. Multiple
+Print warning messages. May be set to
+.Fl W Ns Ar all
+for all warnings,
+.Ar compat
+for groff/troff-compatibility warnings, or
+.Ar syntax
+for syntax warnings. If
+.Fl W Ns Ar error
+is specified, warnings are considered errors and cause utility
+termination. Multiple
.Fl W
arguments may be comma-separated, such as
.Fl W Ns Ar error,all .
.It Fl v
-Make warning and error messages verbose.
+Print verbose parsing output.
.It Ar infile
Read input from
.Ar infile ,
for stdin.
.El
.Pp
+Parsing and validation rules are drawn entirely from the
+.Xr mdoc 7
+and
+.Xr mdoc.samples 7
+manuals.
+.Pp
By default,
.Nm
-reads from stdin and writes to stdout.
+reads from stdin, writes messages to stdout, and writes errors and
+warnings to stderr.
.Pp
.Ex -std mdocml
-.\"
-.Ss Noop Filter
-The default noop
-.Dq validating
-filter simply validates its input; it produces no output beyond error
-and warning messages.
-.\"
-.Ss XML Filter
-The XML filter, specified by
-.Fl f Ar xml ,
-produces correctly-formatted XML output. This filter has no additional
-arguments.
-.Pp
-The XML filter creates an XML document where element names are their respective
-roff macro names. Each element name has an associated
-namespace, which is one of
-.Dq block ,
-.Dq head ,
-.Dq body ,
-or
-.Dq inline ,
-corresponding to the display mode of a node. The document root is
-always the
-.Dq mdoc
-element, in the default namespace; the
-.Dq head
-namespace is for block headers (such as
-.Sq .Ss
-and
-.Sq .Sh ) ;
-the
-.Dq body
-namespace is for block bodies; and the
-.Dq inline
-namespace is for in-line elements (such as
-.Sq .Em ) .
-.\"
-.Ss HTML Filter
-The HTML filter, specified by
-.Fl f Ar html ,
-accepts the following filter-specific arguments:
-.Bl -tag -width "\-c css"
-.It Fl c Ar css
-The CSS file location, which defaults to
-.Ar mdocml.css .
-.It Fl e
-Whether to embed the CSS file into the HTML prologue.
-.El
-.Pp
-By default, the HTML filter produces HTML-4.01 strict mark-up. The
-default CSS document styles the page as it would appear in a terminal
-window.
.\"
.Sh EXAMPLES
-To produce an HTML4-strict document
-.Pa mdocml.html
-for
-.Pa mdocml.1
-with the default, embedded style-sheet:
-.Pp
-.D1 % mdocml -fhtml -e -o mdocml.html mdocml.1
-.Pp
-To create an XML document on standard output from
-.Pa mdocml.1
-with the default namespace identifiers
-.Li head ,
-.Li body ,
-.Li block
-and
-.Li inline :
-.Pp
-.D1 % mdocml -Wall,error -fxml mdocml.1
+To validate this manual page:
.Pp
-The previous example will also halt on input document warnings.
+.D1 % mdocml \-Wall,error mdocml.1
.\"
.Sh SEE ALSO
.Xr groff 1 ,
.Xr mdoc.samples 7 ,
-.Xr mdoc 7
+.Xr mdoc 7 ,
+.Xr mdoc 3
.\" .Sh STANDARDS
.\" .Sh HISTORY
.Sh AUTHORS
.An Kristaps Dzonsons Aq kristaps@kth.se .
.\"
.Sh CAVEATS
-Most caveats of
+The most glaring short-coming of
.Nm
-stem from ambiguities in
-.Xr mdoc 7
-or the necessary limitations of converting an ad hoc language into
-structured ones:
-.Bl -enum -compact -offset indent
-.It
-The engine doesn't understand the
-.Sq \&No ,
-.Sq \&Db ,
-.Sq \&Xc ,
+is that it doesn't yet support the
+.Sq \&Xc
and
.Sq \&Xo
-mdoc macros.
-.It
-All macro arguments may be quoted, instead of only some.
-.It
-Blank lines raise errors.
-.It
-If terminating punctuation is found, then
-.Em all
-remaining tokens are flushed after line scope is closed, not just the
-last one.
-.El
-.Pp
-The roff engine in
-.Nm
-produces text in-line; thus, output may already be partially written by
-the time an error is encountered.
+macros when used to extend the line arguments to
+.Sq \&It ;
+in effect, trampling the body section. We note that this is explicitly
+discouraged in
+.Xr mdoc.samples 7 ,
+but in practice used quite often.
.\" .Sh BUGS
-/* $Id: validate.c,v 1.32 2009/01/16 12:23:25 kristaps Exp $ */
+/* $Id: validate.c,v 1.33 2009/01/16 14:04:26 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
v_post *post;
};
+static int pre_check_parent(struct mdoc *, struct mdoc_node *,
+ int, enum mdoc_type);
+static int pre_check_msecs(struct mdoc *, struct mdoc_node *,
+ int, enum mdoc_msec *);
static int pre_display(struct mdoc *, struct mdoc_node *);
+static int pre_sh(struct mdoc *, struct mdoc_node *);
+static int pre_ss(struct mdoc *, struct mdoc_node *);
static int pre_bd(struct mdoc *, struct mdoc_node *);
static int pre_bl(struct mdoc *, struct mdoc_node *);
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_prologue(struct mdoc *, struct mdoc_node *);
static int pre_prologue(struct mdoc *, struct mdoc_node *);
static int pre_prologue(struct mdoc *, struct mdoc_node *);
static v_pre pres_bd[] = { pre_display, pre_bd, NULL };
static v_pre pres_bl[] = { pre_bl, NULL };
static v_pre pres_it[] = { pre_it, NULL };
+static v_pre pres_ss[] = { pre_ss, NULL };
+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_post posts_bd[] = { headchild_err_eq0, bodychild_warn_ge1, NULL };
static v_post posts_text[] = { elemchild_err_ge1, NULL };
{ pres_prologue, NULL }, /* Os */
/* FIXME: preceding Pp. */
/* FIXME: NAME section internal ordering. */
- /* FIXME: can only be a child of root. */
- { NULL, posts_sh }, /* Sh */
+ { pres_sh, posts_sh }, /* Sh */
/* FIXME: preceding Pp. */
- /* FIXME: can only be a child of Sh. */
- { NULL, posts_ss }, /* Ss */
+ { pres_ss, posts_ss }, /* Ss */
/* FIXME: proceeding... */
{ NULL, posts_pp }, /* Pp */
{ pres_d1, posts_d1 }, /* D1 */
{ NULL, NULL }, /* El */
{ pres_it, posts_it }, /* It */
{ NULL, posts_text }, /* Ad */
- /* FIXME */
+ /* FIXME: argument OR parameters. */
{ NULL, NULL }, /* An */
{ NULL, NULL }, /* Ar */
-
- { NULL, posts_text }, /* Cd */ /* FIXME: section 4 only. */
+ { pres_cd, posts_text }, /* Cd */
{ NULL, NULL }, /* Cm */
{ NULL, posts_text }, /* Dv */
- { NULL, posts_text }, /* Er */ /* FIXME: section 2 only. */
+ { pres_er, posts_text }, /* Er */
{ NULL, posts_text }, /* Ev */
- { NULL, posts_notext }, /* Ex */ /* FIXME: sections 1,6,8 only. */ /* -std required */
+ { pres_ex, posts_notext }, /* Ex */ /* FIXME: -std required */
{ NULL, posts_text }, /* Fa */
{ NULL, NULL }, /* Fd */ /* FIXME: SYNOPSIS section. */
{ NULL, NULL }, /* Fl */
{ NULL, NULL }, /* Nx */
{ NULL, NULL }, /* Ox */
{ NULL, NULL }, /* Pc */
- { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */
+ { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */ /* First should be text. */
{ NULL, NULL }, /* Po */
{ NULL, posts_wline }, /* Pq */ /* FIXME: ignore following Sh/Ss */
{ NULL, NULL }, /* Qc */
};
+static int
+pre_check_msecs(struct mdoc *mdoc, struct mdoc_node *node,
+ int sz, enum mdoc_msec *msecs)
+{
+ int i;
+
+ for (i = 0; i < sz; i++)
+ if (msecs[i] == mdoc->meta.msec)
+ return(1);
+ return(mdoc_nwarn(mdoc, node, WARN_COMPAT,
+ "macro is not appropriate for this manual section"));
+}
+
+
+static int
+pre_check_parent(struct mdoc *mdoc, struct mdoc_node *node,
+ int tok, enum mdoc_type type)
+{
+
+ if (type != mdoc->last->parent->type)
+ return(mdoc_nerr(mdoc, node, "invalid macro parent class %s, expected %s",
+ mdoc_type2a(mdoc->last->parent->type),
+ mdoc_type2a(type)));
+ if (MDOC_ROOT != type && tok == mdoc->last->parent->tok)
+ return(mdoc_nerr(mdoc, node, "invalid macro parent `%s', expected `%s'",
+ mdoc_macronames[mdoc->last->parent->tok],
+ mdoc_macronames[tok]));
+ return(1);
+}
+
+
static int
bodychild_err_eq0(struct mdoc *mdoc)
{
static int
-pre_it(struct mdoc *mdoc, struct mdoc_node *node)
+pre_ss(struct mdoc *mdoc, struct mdoc_node *node)
{
if (MDOC_BLOCK != mdoc->last->type)
return(1);
- assert(MDOC_It == mdoc->last->tok);
+ assert(MDOC_Sh == mdoc->last->tok);
+ return(pre_check_parent(mdoc, node, MDOC_Sh, MDOC_BODY));
+}
- if (MDOC_BODY != mdoc->last->parent->type)
- return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
- if (MDOC_Bl != mdoc->last->parent->tok)
- return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
- return(1);
+static int
+pre_sh(struct mdoc *mdoc, struct mdoc_node *node)
+{
+
+ if (MDOC_BLOCK != mdoc->last->type)
+ return(1);
+ assert(MDOC_Sh == mdoc->last->tok);
+ return(pre_check_parent(mdoc, node, -1, MDOC_ROOT));
+}
+
+
+static int
+pre_ex(struct mdoc *mdoc, struct mdoc_node *node)
+{
+ enum mdoc_msec msecs[3];
+
+ msecs[0] = MSEC_1;
+ msecs[1] = MSEC_6;
+ msecs[2] = MSEC_8;
+ return(pre_check_msecs(mdoc, node, 3, msecs));
+}
+
+
+static int
+pre_er(struct mdoc *mdoc, struct mdoc_node *node)
+{
+ enum mdoc_msec msecs[1];
+
+ msecs[0] = MSEC_2;
+ return(pre_check_msecs(mdoc, node, 1, msecs));
+}
+
+
+static int
+pre_cd(struct mdoc *mdoc, struct mdoc_node *node)
+{
+ enum mdoc_msec msecs[1];
+
+ msecs[0] = MSEC_4;
+ return(pre_check_msecs(mdoc, node, 1, msecs));
+}
+
+
+static int
+pre_it(struct mdoc *mdoc, struct mdoc_node *node)
+{
+
+ if (MDOC_BLOCK != mdoc->last->type)
+ return(1);
+ assert(MDOC_It == mdoc->last->tok);
+ return(pre_check_parent(mdoc, node, MDOC_Bl, MDOC_BODY));
}
assert(TYPE_NONE != type);
if (TYPE_HEAD == type) {
- if (NULL == (n = mdoc->last->data.block.head)) {
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
- return(0);
- } else if (NULL == n->child)
+ n = mdoc->last->data.block.head;
+ assert(n);
+ if (NULL == n->child)
if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
return(0);
- if (NULL == (n = mdoc->last->data.block.body)) {
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
- return(0);
- } else if (NULL == n->child)
+ n = mdoc->last->data.block.body;
+ assert(n);
+ if (NULL == n->child)
if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
return(0);
return(1);
}
- if (NULL == (n = mdoc->last->data.block.head)) {
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
- return(0);
- } else if (NULL == n->child)
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
+ assert(TYPE_BODY == type);
+ assert(mdoc->last->data.block.head);
+
+ n = mdoc->last->data.block.head;
+ assert(n);
+ if (n->child)
+ if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests no line parameters"))
return(0);
- if ((n = mdoc->last->data.block.body) && n->child)
+ n = mdoc->last->data.block.body;
+ assert(n);
+ if (NULL == n->child)
if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
return(0);
if (MDOC_TEXT == mdoc->last->type)
return(1);
- if (MDOC_ROOT == mdoc->last->type)
+ if (MDOC_ROOT == mdoc->last->type) {
+ /* TODO: make sure prologue is complete. */
return(1);
+ }
if (NULL == mdoc_valids[mdoc->last->tok].post)
return(1);